摘要:本文為iOS UIView動畫實踐系列第五篇。詳細介紹Keyframe動畫是如何幫助開發者有效地拆分由若幹段動畫連接配接而成的複雜動畫,并較為精準地定義每段動畫的起始點及持續時間,也讓代碼組織方面變得非常清晰。
CSDN移動将持續為您優選移動開發的精華内容,共同探讨移動開發的技術熱點話題,涵蓋移動應用、開發工具、移動遊戲及引擎、智能硬體、物聯網等方方面面。如果您想投稿、參與内容翻譯工作,或尋求近匠報道,請發送郵件至tangxy#csdn.net(請把#改成@)。
前言
有些時候大家可能會遇到制作複雜、具有連貫性UIView動畫的需求,這時大家可能會使用在
completion
閉包中銜接一段一段的動畫,使之成為一段連續的動畫。
如果我們隻是連接配接2個,或者3個動畫,這種方式或許還行得通,但如果有更多的卡通片段需要連接配接的時候,這種方式會帶來災難性的問題,你的代碼會非常的備援,不斷的在
completion
閉包中嵌套代碼,使代碼維護起來相當的困難。是以今天向大家介紹能更好地實作這個需求的方法,Keyframe動畫。
Keyframe動畫可以讓我們有效的拆分由若幹段動畫連接配接而成的複雜動畫,可以較為精準的定義每段動畫的起始點及持續時間,并且在代碼組織方面也非常清晰。先看看今天要帶大家實作的動畫Demo:
使用場景
我們先來認識一下,在什麼樣的場景下需要使用Keyframe動畫。如圖下所示,這是一個由四段動畫組成的一個複雜動畫,讓UIView沿着長方形的軌迹運動:
我們來看看用代碼如何實作:
[cpp] view plain copy
- UIView.animateWithDuration(1, animations: {
- view.center.x += 200.0
- }, completion: { _ in
- UIView.animateWithDuration(1, animations: {
- view.center.y += 100.0
- }, completion: { _ in
- UIView.animateWithDuration(1, animations: {
- view.center.x -= 200.0
- }, completion: { _ in
- UIView.animateWithDuration(1, animations: {
- view.center.y -= 100.0
- }, completion: nil)
- })
- })
- })
通過上面的僞代碼可以看到,我們使用了
completion
閉包的方式連接配接每一段的動畫,代碼看起來尚且算清晰,可讀性也馬馬虎虎。但是大家想象一下,如果我們使UIView按照一個複雜的路線運作,這一段動畫可能有十幾、幾十段動畫組成的,那麼如果再使用
completion
閉包這種方式連接配接,那代碼是多麼的慘不忍睹。幸好我們有Keyframe動畫,下面就讓我們來看看如何使用Keyframe動畫。
Keyframe動畫
首先我們會使用到UIView的另一個動畫方法
animateKeyframesWithDuration(_: delay: options: animations: completion: )
:
[cpp] view plain copy
- UIView.animateKeyframesWithDuration(2, delay: 0, options: [], animations: {
- // add keyframes
- }, completion: nil)
這個方法的幾個參數與前幾個使用過的動畫方法參數一樣。上面代碼片段的意思是整個關鍵幀動畫的持續時間為2秒、無延遲、無動畫選項、執行完畢後無後續執行的代碼。
注:該方法的動畫選項不再是,而是
UIViewAnimationOptions
。具體的内容大家可以去查閱Apple的文檔。
UIViewKeyframeAnimationOptions
接下來我們要在
animations
閉包中添加關鍵幀了:
[cpp] view plain copy
- UIView.animateKeyframesWithDuration(2, delay: 0, options: [], animations: {
- UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.25, animations: {
- view.center.x += 200.0
- })
- }, completion: nil)
addKeyframeWithRelativeStartTime(_: relativeDuration: animations: )
是UIView添加關鍵幀的方法,該方法有三個參數:
- startTime:關鍵幀開始時間,該時間是相對整個關鍵幀動畫持續時間的相對時間,一般值在0到1之間。如果為0,則表明這一關鍵幀從整個動畫的第0秒開始執行,如果設為0.5,則表明從整個動畫的中間開始執行。
- relativeDuration:關鍵幀持續時間,該時間同樣是相對整個關鍵幀動畫持續時間的相對時間,一般值也在0到1之間。如果設為0.25,則表明這一關鍵幀的持續時間為整個動畫持續時間的四分之一。
- animations:設定視圖動畫屬性的動畫閉包。
我們解釋一下上面這段代碼。整個關鍵幀動畫的持續時間為2秒,第一個關鍵幀從第0秒開始,運作0.5秒結束。下面我們完成其他三個關鍵幀:
[cpp] view plain copy
- UIView.animateKeyframesWithDuration(2, delay: 0, options: [], animations: {
- UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.25, animations: {
- view.center.x += 200.0
- })
- UIView.addKeyframeWithRelativeStartTime(0.25, relativeDuration: 0.25, animations: {
- view.center.y += 100.0
- })
- UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.25, animations: {
- view.center.x -= 200.0
- })
- UIView.addKeyframeWithRelativeStartTime(0.75, relativeDuration: 0.25, animations: {
- view.center.y -= 100.0
- })
- }, completion: nil)
第二個關鍵幀的開始時間為0.25,也就是從整個動畫時間的第0.5時開始執行,同樣持續0.5秒。後兩個關鍵幀的參數就不難了解了。
現在整個代碼看起來非常整潔,條理清晰,可讀性非常好,而且可以有更精确的控制。即使再多幾個關鍵幀也同樣可以從容應對。
關鍵幀動畫不僅僅用于同一個視圖的分段動畫,也可使使用于不同視圖的組合動畫,由于我們還沒講到圖層動畫,是以,開篇的示例動畫中就使用了關鍵幀動畫實作了多個視圖的組合動畫。
示例動畫
在這個示例中雖然看起來是一個紙飛機視圖的連續動畫,但其實是由三個紙飛機視圖組合而成的:
從圖中可以看到其實是有三個紙飛機視圖,隻不過在界面加載之前2号和3号紙飛機視圖的透明度都是為零。
整個動畫是由這三個紙飛機視圖通過關鍵幀動畫組合而成:
圖中标示出了三個飛機視圖的運作軌迹、視圖大小、視圖透明度的狀态,我們來看看代碼如何實作:
[cpp] view plain copy
- let zoomInScaleTransform = CGAffineTransformMakeScale(0.2, 0.2)
- UIView.animateKeyframesWithDuration(3, delay: 0, options: [], animations: {
- UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.2, animations: {
- self.customHeaderView.paperAirplane.center.x += self.view.frame.width
- self.customHeaderView.paperAirplane.center.y += -180
- self.customHeaderView.paperAirplane.transform = zoomInScaleTransform
- })
- UIView.addKeyframeWithRelativeStartTime(0.3, relativeDuration: 0.01, animations: {
- self.customHeaderView.paperAirplaneOpposite.alpha = 1
- self.customHeaderView.paperAirplaneOpposite.transform = zoomInScaleTransform
- })
- UIView.addKeyframeWithRelativeStartTime(0.3, relativeDuration: 0.5, animations: {
- self.customHeaderView.paperAirplaneOpposite.transform = CGAffineTransformIdentity
- self.customHeaderView.paperAirplaneOpposite.center.x -= self.view.frame.width
- self.customHeaderView.paperAirplaneOpposite.center.y += 90
- })
- UIView.addKeyframeWithRelativeStartTime(0.9, relativeDuration: 0.01, animations: {
- self.customHeaderView.paperAirplaneComeBack.alpha = 1
- })
- UIView.addKeyframeWithRelativeStartTime(0.9, relativeDuration: 0.2, animations: {
- self.customHeaderView.paperAirplaneComeBack.center.x += 33
- })
- }, completion: { _ in
- self.restorePaperAirplaneStatus()
- })
大家看到這大家可能會有疑問了,三段動畫怎麼會有五個關鍵幀呢,我們來刨析一下:
- 第一個關鍵幀:完成1号紙飛機視圖運動到右上角并移出螢幕,視圖逐漸變小的動畫。該關鍵幀從整個動畫的第0秒開始執行,持續時間為0.6秒。
- 第二個關鍵幀:由于2号紙飛機視圖的初始透明度為零,是以在第二個關鍵幀将透明度設為1,并且縮小視圖。注意這兩個動作需要在瞬間完成,是以
設為0.01,一個極短的時間。開始時間為整個動畫的第0.9秒開始,較第一個關鍵幀延遲0.3秒。relativeDuration
- 第三個關鍵幀:與第二個關鍵幀同時開始執行,完成2号紙飛機視圖從小變大、并且往左下角運動,一直移出螢幕。持續時間為1.5秒。
- 第四個關鍵幀:與第二個關鍵幀作用相似,改變3号紙飛機視圖的透明度,同樣是在瞬間完成。
- 第五個關鍵幀:與第四個關鍵幀同時執行, 完成向右移動的動畫,持續0.6秒。
關鍵幀完成之後,在
completion
閉包中調用
restorePaperAirplaneStatus()
方法,恢複3個紙飛機視圖的狀态及位置,以便再次執行動畫。
總結
大家在使用關鍵幀動畫時,對于關鍵幀的開始時間和持續時間需要仔細設定,保證每個關鍵幀在合适的時間開始,執行恰當的持續時間。在必要時候也需要在關鍵幀裡修改視圖的一些狀态,但要設定極短的持續時間,表示瞬間完成。
下一篇會向大家介紹在使用Auto Layout的情況下,如何通過限制實作動畫,好了今天就先到這裡吧。