這是 Core Animation 的系列文章母蛛,介紹了 Core Animation 的用法彩郊,以及如何進行性能優(yōu)化蚪缀。
上一篇文章介紹了使用CAAnimation
及其子類創(chuàng)建多種類型動畫抱慌。動畫是一段時間的變化眨猎,因此時間也是一個至關(guān)重要的概念睡陪。這一篇文章介紹 Core Animation 中的時間。
1. CAMediaTiming 協(xié)議
CAMediaTiming
協(xié)議定義了一系列屬性信殊,用于管理動畫過程中的時間涡拘。CALayer
和CAAnimation
都遵守CAMediaTiming
協(xié)議据德,因此,時間可以針對于圖層橱野,也可以針對于動畫水援。
1.1 常用屬性
duration
:是CFTimeInterval
類型茅郎,單位為秒系冗,與NSTimeInterval
類似。指定動畫單次迭代時長成畦。-
repeatCount
:指定動畫重復次數(shù)涝开。如果duration
為2秒舀武,repeatCount
為3.5次,則動畫總時長為7秒瘪匿。duration
和repeatCount
屬性默認均為0,但不意味著動畫時長0秒核偿,播放0次漾岳,這里的0表示使用默認值粉寞,即0.25秒唧垦、1次。 repeatDuration
:指定循環(huán)時間巧还,而非repeatCount
的循環(huán)次數(shù)双炕。autoreverses
:如果為true妇斤,則向前播放后后退播放。默認為false荸恕。
1.2 使用 repeatDuration 和 autoreverses 實現(xiàn)擺動動畫
下面代碼實現(xiàn)了無限擺動門的動畫融求。其中算撮,使用autoreverses
實現(xiàn)自動反轉(zhuǎn)擺動肮柜,設(shè)置repeatDuration
為infinity
實現(xiàn)永不停歇擺動。repeatCount
和repeatDuration
可能沖突莱睁,同時使用的結(jié)果可能是未定義的仰剿。
private func swingingDoorUsingAutoreverse() {
doorLayer.bounds = CGRect(x: 0, y: 0, width: 128, height: 256)
// 設(shè)置錨點為layer左邊緣中點
doorLayer.anchorPoint = CGPoint(x: 0, y: 0.5)
doorLayer.contents = UIImage(named: "Door")?.cgImage
view.layer.addSublayer(doorLayer)
// Apply perspective transform
var perspective = CATransform3DIdentity
perspective.m34 = -1.0 / 500.0
view.layer.sublayerTransform = perspective
// Apply swinging animation
let animation = CABasicAnimation(keyPath: "transform.rotation.y")
animation.toValue = -.pi/2.0
animation.duration = 2.0
animation.repeatDuration = .infinity
animation.autoreverses = true
doorLayer.add(animation, forKey: nil)
}
效果如下:
1.3 相對時間
Core Animation 中的時間是相對時間南吮,每個動畫有自身的時間概念旨袒,可以獨立的加速术辐、延時或偏移辉词。
beginTime
屬性指定動畫開始之前的延遲時間,動畫起點從添加到可見圖層開始計算敷搪。默認值為0幢哨,即添加到可見圖層后動畫立即開始捞镰。
speed
是時間的倍數(shù),默認為1.0践樱。speed
減小拷邢,動畫屎慢、圖層時間會變慢腻惠;speed
增大,動畫廷臼、圖層時間會變快荠商。如果speed
為2.0,對于一個duration
為1秒的動畫初肉,實際上0.5秒就會完成饰躲。如果為 layer 的speed
是2.0嘹裂,則所有添加到該 layer 的速度都會變?yōu)槎丁H绻麆赢?code>speed是3丁寄,layer 的speed
是0.5伊磺,則動畫是正常速度的1.5倍删咱。
timeOffset
指定時間偏移量痰滋。beginTime
延遲動畫開始時間,timeOffset
快進到指定時間徊哑。例如莺丑,動畫時長1秒墩蔓,timeOffset
為0.5秒意味著動畫從0.5秒開始奸披,執(zhí)行到結(jié)束后再次從0秒執(zhí)行到0.5秒阵面。
與beginTime
不同洪鸭,timeOffset
不受speed
影響览爵。例如镇饮,動畫duration
時間為1秒储藐,設(shè)置speed
為2.0钙勃、timeOffset
為0.5,此時動畫時長變?yōu)?.5秒左医。timeOffset
讓動畫從結(jié)束位置開始同木,但仍然會播放一個完整的時長(這里時0.5秒)彤路,這個動畫僅僅是從結(jié)尾開始播放洲尊,最終完成了一個循環(huán)奈偏。
使用下面的示例設(shè)置不同的speed
惊来、timeOffset
裁蚁,點擊 Play 按鈕查看播放效果:
private func createShip() {
// Create a path
let centerX = view.bounds.size.width / 2
let centerY = view.bounds.size.height / 2
bezierPath.move(to: CGPoint(x: view.bounds.size.width / 2 - 150, y: centerY))
bezierPath.addCurve(to: CGPoint(x: centerX + 150, y: centerY), controlPoint1: CGPoint(x: centerX - 75, y: centerY - 150), controlPoint2: CGPoint(x: centerX + 75, y: centerY + 150))
// Draw the path using a CAShapeLayer
let pathLayer = CAShapeLayer()
pathLayer.path = bezierPath.cgPath
pathLayer.fillColor = UIColor.clear.cgColor
pathLayer.strokeColor = UIColor.red.cgColor
pathLayer.lineWidth = 3.0
view.layer.addSublayer(pathLayer)
// Add the ship
shipLayer.bounds = CGRect(x: 0, y: 0, width: 64, height: 64)
shipLayer.position = CGPoint(x: centerX - 150, y: centerY)
shipLayer.contents = UIImage(named: "Ship")?.cgImage
view.layer.addSublayer(shipLayer)
updateSliders()
}
@IBAction func updateSliders() {
let timeOffset = timeOffsetSlide.value
timeOffsetLabel.text = String(format: "%.2f", timeOffset)
let speed = speedSlide.value
speedLabel.text = String(format: "%.2f", speed)
}
@IBAction func playButtonTapped(_ sender: Any) {
// Create the keyframe animation
let animation = CAKeyframeAnimation(keyPath: "position")
animation.timeOffset = CFTimeInterval(timeOffsetSlide.value)
animation.speed = speedSlide.value
animation.duration = 1.0
animation.path = bezierPath.cgPath
animation.rotationMode = .rotateAuto
animation.isRemovedOnCompletion = true
shipLayer.add(animation, forKey: nil)
}
效果如下:
1.4 fillMode
fillMode
屬性可以管理動畫開始矮男、結(jié)束時的表現(xiàn)毡鉴。
1.4.1 removed
fillMode
默認值為CAMediaTimingFillMode.removed
。如果設(shè)置了beginTime
管削,動畫從beginTime
開始含思;如果沒有設(shè)置甘晤,則立即開始线婚。動畫結(jié)束后移除動畫塞弊。
1.4.2 backwards
無論是否立即執(zhí)行動畫,CAMediaTimingFillMode.backwards
都會展示動畫的第一幀饰抒。
1.4.3 forwards
CAMediaTimingFillMode.forwards
像往常一樣播放動畫袋坑,但將動畫的最后一幀保留到屏幕上枣宫,直到你移除動畫也颤。
1.4.4 both
CAMediaTimingFillMode.both
是forwards
和backwards
的組合翅娶,即立即顯示動畫的第一幀范咨,動畫結(jié)束后保留最后一幀渠啊。
如果想保留上一部分動畫結(jié)束狀態(tài)替蛉,可以設(shè)置動畫為
forwards
,并設(shè)置isRemovedOnCompletion
為 false译柏。
那我們是否需要使用fillMode
姐霍?是否應(yīng)該移除動畫镊折?應(yīng)如何更新圖層以實現(xiàn)平滑動畫?
最重要的一條原則就是:移除動畫并盡可能避免使用fillMode
骂因,除非要實現(xiàn)的效果沒有其他方式可以實現(xiàn)寒波。fillMode
會使UI元素失去交互性俄烁,并且圖層與model不匹配僚碎。一般在添加動畫后立即更新圖層 model 即可勺阐。如果出現(xiàn)動畫起始矛双、終止時閃爍议忽,可以嘗試添加動畫前更新 model栈幸。
2. 時間層級關(guān)系
在第一篇文章CoreAnimation基本介紹中介紹了圖層層級結(jié)構(gòu),動畫時間也有類似結(jié)構(gòu)玩焰。
每個動畫昔园、圖層有自身時間概念默刚。調(diào)整一個圖層的時間會影響其自身及其子類的動畫,但不會影響父圖層澜搅。
2.1 全局時間和本地時間
Core Animation 的全局時間也稱為馬赫時間(mach time)勉躺,mach 是iOS秃流、macOS系統(tǒng)內(nèi)核名稱舶胀。在同一設(shè)備的不同線程嚣伐、進程中,mach time 相同放典;不同設(shè)備 mach time 不同奋构。mach time 可以作為動畫時間參照點拱层。使用以下函數(shù)獲取馬赫時間:
let machTime = CACurrentMediaTime()
CACurrentMediaTime()
具體值無關(guān)緊要根灯,真實作用在于為動畫時間測量提供了一個相對值烙肺。CACurrentMediaTime()
不能用于更新時鐘時間。
每個CALayer
桃笙、CAAnimation
實例有自身時間概念怎栽,是根據(jù)父圖層、動畫層級中的beginTime
谬以、timeOffset
和speed
等屬性計算出。就跟轉(zhuǎn)換不同layer間坐標系統(tǒng)一樣由桌,CALayer
也提供了轉(zhuǎn)換不同圖層之間的本地時間方法:
-
convertTime:fromLayer:
將 time interval 從指定 layer 轉(zhuǎn)換為接收者時間空間为黎。 -
convertTime:toLayer:
將 time interval 從接收者空間轉(zhuǎn)換為指定 layer 時間空間。
想要同步多個具有不同speed
行您、timeOffset
或beginTime
的圖層時铭乾,上述方法會非常有效。
2.1 暫停娃循、回退和快進
動畫speed
被設(shè)置為零會暫停動畫炕檩,但動畫被添加到圖層后不能修改,因此不能使用speed
屬性暫停運行中的動畫捌斧。添加CAAnimation
到 layer 時,復制一份不可變動畫對象捞蚂,因此修改原始動畫對進行中的動畫沒有效果妇押。使用animation(forKey:)
可以獲取進行中的動畫,但不能修改獲取的動畫姓迅,修改該動畫會導致無法預期的行為敲霍。
從圖層移除進行中的動畫,動畫會急速返回動畫之前的狀態(tài)丁存。在動畫被移除前肩杈,復制 presentation layer 的屬性,并賦值給 model layer解寝,動畫就會有看起來停止了的效果锋恬。
增大主window layer 的speed
,可以加快整個應(yīng)用動畫速度编丘,達到快進效果。設(shè)置speed
為負值彤悔,可以達到回退效果嘉抓。在模擬器中,勾選 Debug > Slow Animations 后可以開啟慢動畫晕窑,可以用于調(diào)試動畫效果抑片。
Demo名稱:CoreAnimation
源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/CoreAnimation
上一篇:CAAnimation:屬性動畫CABasicAnimation、CAKeyframeAnimation以及過渡動畫杨赤、動畫組
下一篇:計時器CADisplayLink
參考資料:
歡迎更多指正:https://github.com/pro648/tips
本文地址:https://github.com/pro648/tips/blob/master/sources/圖層時間CAMediaTiming.md