優(yōu)雅的動效是一個好 App 的基礎(chǔ)史飞,它可能直接決定了應用的用戶體驗干像。好在 iOS 給我們提供了多種實現(xiàn)動畫效果的方式柱查。本文將會比較綜合地談談在 iOS 中實現(xiàn)動畫的方法和小技巧(aka. Tips & Tricks)
為了方便演示知残,我首先在 Interface Builder 中拖拉了一些控件靠瞎,那個藍色的方塊就是今天的主角~
你可能注意到了,藍色方塊還被我添加了約束求妹,目的是為了下文演示約束動畫乏盐。
每錯,這個藍色方塊被添加了兩個能夠決定 X 坐標的約束制恍,一個被暫時 disabled 了父能。
UIView Animation
Basics
首先介紹的第一個動畫方式是 UIKit 給我們提供的封裝方式,也就是下面幾個類方法:
animateWithDuration(duration:, delay:, usingSpringWithDamping:, initialSpringVelocity, options, animations:, completion:)
animateWithDuration(duration:, animations:)
transitionWithView(view:, duration:, options:, animations:, completion:)
- ...
使用起來很簡單净神,只需要將動畫的最終狀態(tài)修改在 animation
閉包中即可何吝,并且還有多種參數(shù)可供選擇。下面是例子:
@IBAction func performTransformAnimation(sender: AnyObject) {
UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10, options: .CurveEaseOut, animations: {
self.squareView.transform = CGAffineTransformMakeScale(2.0, 2.0)
}) { _ in }
}
可以發(fā)現(xiàn)鹃唯,其實 iOS 系統(tǒng)級的很多動畫都是用上面的方法實現(xiàn)的爱榕,早在 iOS 6 及以前,還沒有 spring 動畫坡慌,于是系統(tǒng)很多的動畫看起來都像是沒有緩動一樣(iOS 自帶緩動效果不強)黔酥,到了 iOS 7 以后,系統(tǒng)提供了一個新的 API洪橘,支持 spring 效果跪者,這種效果就是大家現(xiàn)在看到的應用開啟、UINavigationController
Push 和 Pop熄求,以及控制中心彈簧效果渣玲。有關(guān)于 spring 動畫的參數(shù),本文不細解釋了弟晚,大家可以自己 fine-tune 一下忘衍,WWDC 有個 Session 也講了這個動畫的細節(jié)。
約束動畫
上面提到我創(chuàng)建了兩個約束卿城,下面來演示淑履,如何在約束被修改時產(chǎn)生動畫效果。
@IBAction func performConstraintAdjustmentAnimation(sender: AnyObject) {
UIView.animateWithDuration(0.6, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 10, options: .CurveEaseOut, animations: {
self.centerConstraint.active = false
self.leadingConstraint.active = true
self.view.layoutIfNeeded()
}) { _ in }
}
都是套路藻雪,唯一值得注意的是秘噪,我們需要在閉包內(nèi)顯式調(diào)用 superview 的 layoutIfNeeded()
方法,因為約束在被修改后需要在下一個 RunLoop 周期中剩下勉耀,也就是說指煎,閉包內(nèi)的設(shè)置完成后蹋偏,view 的 frame
并沒有發(fā)生改變,所以動畫就不能產(chǎn)生至壤,但是如果我們強制重新布局威始,frame
在閉包內(nèi)發(fā)生改變了,動畫就能夠產(chǎn)生了像街。
Transition
顧名思義黎棠,這東西是轉(zhuǎn)場效果,也就是兩個毫無關(guān)聯(lián)的狀態(tài)可以用一種“華麗”的動效來切換镰绎,如果說上面那些方法就像是 Keynote 中的 『Magic Move』脓斩,那么這種方法則是其他更炫酷多變的效果。
下面演示一下:
@IBAction func squareViewDidTap(sender: AnyObject) {
self.squareView.backgroundColor = UIColor.redColor()
UIView.transitionWithView(self.squareView, duration: 1.0, options: .TransitionFlipFromLeft, animations: nil) { _ in }
}
這里有個坑畴栖,我嘗試將顏色設(shè)置寫在閉包內(nèi)随静,發(fā)現(xiàn)沒有效果,寫在外面卻有效果了吗讶,這個具體我也沒有搞清是什么原因燎猛,總之 transition 就是將兩個狀態(tài)分別截圖,然后將其作為貼圖貼在一個 OpenGL 物體上照皆,然后進行動畫重绷,一般用到的情況比較少。
CoreAnimation
上面說到的是 UIKit 給我們封裝的方法膜毁,但是它的局限性也顯而易見昭卓,比如不能終止、不能循環(huán)播放等等爽茴。有時我們需要更靈活地動畫葬凳,這就需要用到 CoreAnimation 了绰垂,它的 API 是 iOS / OS X 統(tǒng)一的室奏,所以可以無損移植。實際上我們在 iPhone 上看到的一切都是 CALayer
劲装,UIView
只不過是它的一個代理和事件響應者罷了胧沫,本身并不具有顯示功能。
但是這里要注意的是占业,CoreAnimation 的任何動畫都是幻象绒怨,都是幻象,都是幻象谦疾。那啥說三遍南蹂。而 UIView
的動畫是真正改變狀態(tài)的,這里一定要注意念恍。就算我們設(shè)置了 removedOnCompletion = false
六剥,也只是幻象終止在了最后一幀罷了晚顷,實際屬性是沒有任何改變的!疗疟!
我們一般用 CoreAnimation 做一些提醒式或者增彩式的動畫该默,這些動畫通常可有可無策彤。下面演示用 CAKeyframeAnimation
實現(xiàn)搖晃動畫:
@IBAction func performCoreAnimation(sender: AnyObject) {
let shakeAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
shakeAnimation.values = [0, -20, 18, -16, 12, -5, 2, 0]
shakeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
shakeAnimation.duration = 0.9
self.squareView.layer.addAnimation(shakeAnimation, forKey: "")
}
使用 CAKeyframeAnimation
我們可以像在“曾經(jīng)的” Flash 里一樣栓袖,做一些關(guān)鍵幀動畫,十分方便店诗,同時還可以使用組動畫裹刮,組合多個屬性。
TimingFunction
緩動函數(shù)必搞,這個在 iOS 中很有意思必指,你除了可以使用系統(tǒng)自帶的幾種緩動效果以外,還可以自己調(diào)整它的參數(shù)恕洲,做出自己的緩動曲線塔橡,而這種曲線的生成竟然和 CSS3 的緩動函數(shù)一樣,使用兩個控制點霜第,拉出一條曲線葛家,這條曲線就反映了時間渡過的快慢。
所以我嘗試順手寫一個可視化的曲線創(chuàng)建器泌类,看看效果:
有關(guān)這個曲線創(chuàng)建器癞谒,主要就是幾個 CAShapeLayer
,然后修改 path
屬性使其吻合最終的數(shù)值刃榨,下面是一些關(guān)鍵代碼:
開源動畫庫
如果上面兩種方法還不能滿足你的需求弹砚,那么恭喜你,GitHub 上有好多資源等著你枢希!
下面簡單剖析一下比較火的開源庫桌吃,來自 Facebook 的 Pop。對于這個庫的使用我不闡釋了苞轿,大家可以去看它的文檔茅诱。我們這里分析分析它是怎么實現(xiàn)動畫的。
在 POPAnimator.mm
中我們可以發(fā)現(xiàn)它使用了 DisplayLink 這種技術(shù)搬卒,使用它瑟俭,我們可以輕松地捕捉到每次屏幕刷新,也就是說每次屏幕刷新一下契邀,我們就更新一下被動畫對象的狀態(tài)摆寄,實際上就是 Pop 自己用 DisplayLink 實現(xiàn)了一個高精度的 Timer,然后常規(guī)地設(shè)置屬性來達到動畫目的。實際上除了系統(tǒng)的 CoreAnimation 以外微饥,絕大多數(shù)開源庫都用了這個方法锐帜。
OK,就先分享這么多~