這是「邏輯思維」團隊的「得到」App,把錢存進去會在「我的帳戶」裡面看到一個餘額的數字動畫。
而之前在「餘額寶」的App中也有看到類似的動畫效果,給App增加了不少樂趣,這次打算實作看看。
如果今天PM告訴我們需要一個「從0過渡到1024的動畫效果」,我想有些人會覺得很簡單,直接做一個循環,在畫面上顯示1、2、3、4...1024。
但其實這種動畫通常不會顯示很久,測試下來感覺2秒就開始考驗耐性了。
所以其實應該是需要寫成像「進度條」那樣,根據當前進度跳躍式的重新渲染畫面(如0、124、258...1024),這樣一定比1、2、3、4...1024每一個數字都要進行一次渲染來得有效率。
畢竟應該沒有PM會說,「我們希望用戶可以在1秒內看到每一個數字」...
定義「初始數字」、「最終數字」、「動畫時間」、啟動「Timer」
func count(from: Float, to: Float, duration: TimeInterval? = nil) {
startingValue = from
destinationValue = to
timer?.invalidate()
timer = nil
if (duration == 0.0) {
// No animation
setTextValue(value: to)
return
}
progress = 0.0
totalTime = duration ?? animationDuration
lastUpdateTime = Date.timeIntervalSinceReferenceDate
addDisplayLink()
}
func countFromCurrent(to: Float, duration: TimeInterval? = nil) {
count(from: currentValue, to: to, duration: duration ?? nil)
}
Timer所在的runloop中因為需要處理很多事物,所以它最小週期大約在50~100ms之間(官方描述),也就是1秒內大約能處理20次,這樣不容易達到60FPS的順暢感。
timer = CADisplayLink(target: self, selector: #selector(self.updateValue(timer:)))
// 相當於Timer的fire(),開始執行
timer?.add(to: .main, forMode: .defaultRunLoopMode)
// 防止tableView等畫面拖動時的影響。
timer?.add(to: .main, forMode: .UITrackingRunLoopMode)
timer會呼叫「updateValue」方法,來更新當前數字的「進度」,並且通過settextValue()來改變UILabel中的
@objc fileprivate func updateValue(timer: Timer) {
let now: TimeInterval = Date.timeIntervalSinceReferenceDate
progress += now - lastUpdateTime
lastUpdateTime = now
if progress >= totalTime {
self.timer?.invalidate()
self.timer = nil
progress = totalTime
}
setTextValue(value: currentValue)
}
// update UILabel.text
fileprivate func setTextValue(value: Float) {
text = String(format: "$ %.1f", value)
}
以時間為單位,如果progress已經達到/超過動畫時間,就直接顯示最終的數字。
fileprivate var currentValue: Float {
if progress >= totalTime { return destinationValue }
return startingValue + Float(progress / totalTime) * (destinationValue - startingValue)
}