Posted at

UILabelでエレベーター風なアニメーション

More than 3 years have passed since last update.


はじめに

ラベルの数字がエレベーター風に昇降アニメーションする実装をしてみました。

完成イメージは下のような感じ↓


環境


  • XCode7.2 (swift2.1)


実装


アニメーションするラベル

primaryLabelsecondaryLabelの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にあります。

PMElevateLabel - github