はじめに
ラベルの数字がエレベーター風に昇降アニメーションする実装をしてみました。
完成イメージは下のような感じ↓
環境
- XCode7.2 (swift2.1)
実装
アニメーションするラベル
primaryLabel
とsecondaryLabel
の2つのラベルを用意して、originをアニメーションさせていきます。
PMElevateLabel.swift
import UIKit
class PMElevateLabel: UIView {
//MARK: - Properties
//MARK: Public
var duration : NSTimeInterval = 1.0
//MARK: Private
private var currentValue : Int = 0
private var primaryLabel : UILabel!
private var secondaryLabel : UILabel!
//MARK: - Initializer
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.masksToBounds = true
primaryLabel = self.label(CGRectMake(0, 0, frame.width, frame.height))
self.addSubview(primaryLabel)
secondaryLabel = self.label(CGRectMake(0, 0, frame.width, frame.height))
self.addSubview(secondaryLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Public method
func setValue(value : Int, animated : Bool) {
if self.currentValue == value {
return
}
self.primaryLabel.text = "\(self.currentValue)"
self.secondaryLabel.text = "\(value)"
let destOffset = (self.currentValue > value) ? -self.frame.height : self.frame.height
self.primaryLabel.setOrigin(.zero)
self.secondaryLabel.setOrigin(CGPointMake(0, destOffset))
UIView.animateWithDuration((animated) ? self.duration : 0.0, delay: 0.0, options: .CurveLinear, animations: { () -> Void in
self.primaryLabel.setOrigin(CGPointMake(0, -destOffset))
self.secondaryLabel.setOrigin(.zero)
}, completion: { _ in })
self.currentValue = value
}
//MARK: - Private method
private func label(frame : CGRect) -> UILabel {
let label = UILabel(frame: frame)
label.backgroundColor = UIColor.clearColor()
label.textAlignment = .Center
label.font = UIFont.systemFontOfSize(50)
return label
}
}
extension UIView {
func setOrigin(origin : CGPoint) {
var rect = self.frame
rect.origin = origin
self.frame = rect
}
}
アニメーションさせる
今回はCADisplayLink
を使って適当にアニメーションさせます。
ViewController.swift
import UIKit
class ViewController: UIViewController {
//MARK: - Properties
//MARK: Public
//MARK: Private
private var elevateLabel : PMElevateLabel!
private var animationStartTime : CFTimeInterval?
//MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
elevateLabel = PMElevateLabel(frame: CGRectMake(0, 0, 100, 100))
elevateLabel.center = CGPointMake(self.view.frame.width / 2, self.view.frame.height / 2)
elevateLabel.layer.borderColor = UIColor.blueColor().CGColor
elevateLabel.layer.borderWidth = 1.0
self.view.addSubview(elevateLabel)
self.startAnimation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK: - Private method
private func startAnimation() {
let displayLink = CADisplayLink(target: self, selector: "updateDisplayLink:")
displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
self.animationStartTime = CACurrentMediaTime()
}
//MARK: - CADislayLink handler
@objc private func updateDisplayLink(displayLink : CADisplayLink) {
guard let animTime = self.animationStartTime else {
displayLink.invalidate()
return
}
let elapsedTime = displayLink.timestamp - animTime
if elapsedTime >= 30 {
displayLink.invalidate()
}
elevateLabel.setValue(Int(elapsedTime), animated: true)
}
}
終わりに
やっつけですが、UILabelを使ったお手軽アニメーションを実装してみました。
現状は数値を渡して、それをもとにアニメーションするだけです。
といってもCADisplayLink
やタイマーなどで定期的に呼ばないとそれっぽくならないので、あらかじめ配列でデータを渡しておいたり、アニメーションの実装を全て内包してしまったほうが使いやすい気はしてます。
アニメーション自体もかなりシンプルなので、Transformとかその他装飾をしてあげればもうちょっとかっこよくなると思います。
ソースコードは下記githubにあります。