はじめに
UILabelを1文字ずつ動かして、ゲームのローディング画面っぽい表示をさせてみる。
完成図は下記の画像のような感じ↓
実装
順を追って説明していきます。
今回はswiftで実装します。
クラスの作成
UIViewのサブクラスを作成します。
(AnimationLabel
だとUILabelのサブクラスっぽい印象があるので、AnimationLabelView
という名前にしました)
import UIKit
class AnimationLabelView: UIView {
}
プロパティの追加
クラスのプロパティをUILabelに合わせる(似せる)ことで違和感無く使用することができます。
使用イメージとしては、UILabelを使う感覚でオブジェクトを生成したあと、最後にアニメーション用のメソッドを呼んでもらう感じにしたい。
外部から操作されたくない変数は全部privateにしまっちゃいます。
//MARK: - Public Property
var roopCount : NSInteger = 0 //アニメーションのループ回数。0で無限ループとする
var text : NSString = ""
var font : UIFont = UIFont.boldSystemFontOfSize(17)
var textColor : UIColor = UIColor.blackColor()
var animationOffset : CGFloat = 5 // ラベルが上にバウンスする量(ピクセル)
var animationDuration : Double = 0.2 // ラベル1つあたりのアニメーション時間
var animationDelay : Double = 0.1 // ラベル間のアニメーションの時差
//MARK: - Private Property
private var contentView : UIView = UIView()
private var stopAnimationFlag : Bool = false
private var currentRoopCount : NSInteger = 0
private var animationIndex : NSInteger = 0
private var labelArray : Array<UILabel> = Array<UILabel>()
定数
ラベル間のマージンを定数で置いておきます。
(プロパティから操作できるようにしても良いかも)
private let LABEL_MARGIN : CGFloat = 1.0
クラスメソッドの追加
クラスメソッドは極力シンプルかつ、少なくしておきましょう。
//MARK: - Class Method
func startAnimation(){
self.reloadView()
self.executeAnimation()
}
func stopAnimation(){
self.stopAnimationFlag = false
}
プライベートメソッドの追加
Viewの実装
//MARK: - Private Method
//パラメータ類の初期化
private func initializeView(){
for subView : UILabel in self.labelArray {
subView.removeFromSuperview()
}
labelArray = Array<UILabel>()
currentRoopCount = 0
stopAnimationFlag = false
contentView = UIView(frame: CGRectMake(0, 0, 10, 10))
contentView.backgroundColor = UIColor.clearColor()
self.addSubview(contentView)
}
//Viewの描画
private func reloadView(){
self.initializeView()
var offsetX : CGFloat = 0
var labelHeight : CGFloat = 0
//textにセットした文字列を1文字ずつに分解し、UILabelに1文字ずつ格納していく
for var i = 0; i < text.length; i++ {
let unitString = text.substringWithRange(NSMakeRange(i, 1))
var label : UILabel = UILabel(frame: CGRectMake(offsetX, 0, 10, 10))
label.backgroundColor = UIColor.clearColor()
label.text = unitString
label.font = self.font
label.textColor = self.textColor
label.sizeToFit()
self.contentView.addSubview(label)
self.labelArray.append(label)
offsetX += label.frame.size.width + LABEL_MARGIN
if (labelHeight < CGRectGetHeight(label.frame)) {
labelHeight = CGRectGetHeight(label.frame)
}
}
self.contentView.frame = CGRectMake(0, 0, offsetX, labelHeight)
self.contentView.center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2)
}
//アニメーションの実行
private func executeAnimation(){
var delayInSeconds : Double = 0.0
var duration : Double = self.animationDuration
self.animationIndex = 1
//dispatch_afterで徐々にアニメーション開始のタイミングをずらしていく
for label : UILabel in self.labelArray {
let seconds = delayInSeconds * Double(NSEC_PER_SEC)
let popTime : dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds))
dispatch_after(popTime, dispatch_get_main_queue(), { () -> Void in
UIView.animateWithDuration(duration, animations: { () -> Void in
var labelFrame : CGRect = label.frame
labelFrame.origin.y -= self.animationOffset
label.frame = labelFrame
}, completion: { (finished : Bool) -> Void in
UIView.animateWithDuration(duration, animations: { () -> Void in
var labelFrame : CGRect = label.frame
labelFrame.origin.y += self.animationOffset
label.frame = labelFrame
}, completion: { (finished : Bool) -> Void in
if (self.animationIndex == self.labelArray.count) {
self.didFinishAnimation()
}else{
self.animationIndex++
}
})
})
})
delayInSeconds += self.animationDelay
}
}
//アニメーション完了後のハンドラ
private func didFinishAnimation(){
if (self.stopAnimationFlag == true) {
return
}
if (self.roopCount <= 0){
self.executeAnimation()
}else if (self.currentRoopCount < self.roopCount){
self.executeAnimation()
self.currentRoopCount++;
}
}
Viewの追加(使用例)
var animationLabel : AnimationLabelView = AnimationLabelView(frame: CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 100))
animationLabel.text = "Loading..."
animationLabel.font = UIFont.boldSystemFontOfSize(40)
animationLabel.center = CGPointMake(CGRectGetWidth(self.view.frame) / 2, CGRectGetHeight(self.view.frame) / 2)
self.view.addSubview(animationLabel)
animationLabel.startAnimation()
ソースコードは下記githubにて公開しております。
PMAnimationLabelView_swift - GitHub
終わりに
途中でラベルにテキストをセットし直したとしても、再度startAnimation()
を呼ぶ事でviewが再描画され、表示を更新することができます。
executeAnimation()
のアニメーション部分をいじるればいろいろなアニメーションを実装することができます。
この実装だとstopAnimation()
ですぐにアニメーションを停止できないので、させたい場合はCoreAnimationを使った方が良いのかなぁと思います。