這篇我們來討論下Core Animation的原理和在iOS開發(fā)中的應(yīng)用.
Core Animation原理
Core Animation是用來展示view或者其他可見元素動(dòng)畫的機(jī)制顿膨。有了Core Animation(以下簡(jiǎn)稱CA), 開發(fā)人員只需要配置一些簡(jiǎn)單的動(dòng)畫參數(shù)就能夠創(chuàng)建出流暢的原生動(dòng)畫仗考,例如開始位置,結(jié)束位置(basic core animation類)等等. 在Apple的框架中,Core Animation所處位置如下圖所示:
可以看到继控,CA位于UIKit和AppKit下方,算是一個(gè)較為底層的框架。CA存在于QuartzCore framework,這里面也包含一些OpenGL和Graphics的子框架贯被。這些框架會(huì)將生成的動(dòng)畫、圖片等圖像信息傳給底層的繪圖硬件(caching成bitmap),產(chǎn)生最終用戶看到的界面彤灶。
CA涉及兩個(gè)部分看幼,一是CALayer, 另一個(gè)是Animation. 我們先來講CALayer。
CALayer
在iOS中枢希,UIView是我們我們最常用的一個(gè)類桌吃,整個(gè)的UIView是建立在CA層上的,每個(gè)UIView及其子類都存在一個(gè)layer層(root layer)苞轿。這個(gè)層是干什么的呢?
相信你們或多或少都寫過這樣的代碼吧:
demoLabel.layer.cornerRadius = 5.f;
demoLabel.layer.borderColor = []UIColor grayColor];
demoLabel.layer.borderWidth = 1.f;
代碼中的layer就是CALayer逗物。 CALayer是用來管理布局屬性的搬卒,即用戶可見部分,也就是說翎卓,我們看到的一個(gè)個(gè)label, textView契邀,button都是CALayer幫助我們畫出的,而UIView失暴,UITextView以及UIButton都是CALayer的一個(gè)封裝坯门,為其提供了事件響應(yīng)能力。當(dāng)然逗扒,平常我們寫代碼時(shí)古戴,一些簡(jiǎn)單的可見屬性例如background color是可以用UIView本身來設(shè)置的,但是一旦涉及到復(fù)雜精細(xì)的界面效果, 則非CALayer莫屬矩肩。
在本篇中现恼,我們不講述CALayer是如何在iOS和OSX中的布局方式和不同的坐標(biāo)系統(tǒng),若您想了解的話黍檩,可點(diǎn)擊以下兩個(gè)鏈接: 有<a href="http://www.reibang.com/p/c6714dda9a9c">中文版</a>和<a >英文版</a>可供選擇叉袍,英文是Apple Doc里的,官方內(nèi)容; 中文版來自簡(jiǎn)書刽酱。在本篇中喳逛,我們偏向更理論的部分。
和UIView一樣棵里,layer也有樹形結(jié)構(gòu)润文,也有sublayer, superlayer之分衍慎。我們可以參考下圖:
如圖所示转唉,我們可以像添加subview一樣添加sublayer,最終產(chǎn)生的效果是一樣的稳捆。 而且右側(cè)的layer樹也和UIView的層級(jí)樹一樣自頂向下赠法。本質(zhì)上說,UIView的層級(jí)樹都會(huì)轉(zhuǎn)換成layer的樹形結(jié)構(gòu), 交給底層的繪圖硬件去處理,性能也因此得到了極大的提升砖织。
CALayer,或者說我們經(jīng)常說的layer款侵,本質(zhì)上是一個(gè)model tree,它是系統(tǒng)用來存儲(chǔ)當(dāng)前屬性的object侧纯,例如一些動(dòng)畫的target value新锈, 起始值,結(jié)束值等等眶熬。但是妹笆,在CA中的CALayer可不止一個(gè)model tree,還有2個(gè)娜氏,分別是presentation tree和render tree拳缠。它們3個(gè)分別反映了動(dòng)畫狀態(tài)的不同方面。我們之前已經(jīng)說過model tree用來存儲(chǔ)一些靜態(tài)屬性贸弥,平常我們的賦值操作就是直接和model tree進(jìn)行交互窟坐。與此同時(shí),presentation和render tree中也生成了和model tree相同的對(duì)應(yīng)樹形結(jié)構(gòu),為將來動(dòng)畫展示做準(zhǔn)備绵疲。不同的是哲鸳,render tree是私有的object, 對(duì)我們不可見,被用來渲染動(dòng)畫(數(shù)據(jù)), 其運(yùn)行的線程在比當(dāng)前app主線程優(yōu)先級(jí)還高盔憨,是在Apple的render server進(jìn)程中進(jìn)行的渲染徙菠,不會(huì)造成主線程堵塞。細(xì)心地你當(dāng)然也會(huì)發(fā)現(xiàn)這樣會(huì)增加進(jìn)程間通訊的開銷般渡,所以Apple不建議開發(fā)者手動(dòng)commit transaction而是默認(rèn)在run loop中提交事務(wù)(進(jìn)程間傳遞的數(shù)據(jù)主要有整個(gè)render tree和對(duì)應(yīng)的動(dòng)畫)懒豹。 三種tree關(guān)系如下圖所示:
CALayer中還有個(gè)比較重要的概念就是它具有層級(jí)關(guān)系的時(shí)間系統(tǒng), CALayer實(shí)現(xiàn)了CAMediaTiming協(xié)議,用來控制動(dòng)畫的展示等驯用,例如暫停和恢復(fù)脸秽。這部分內(nèi)容我們另起一篇講.
Animation
眾所周知,Apple的對(duì)動(dòng)畫的處理真是流暢之極蝴乔,細(xì)膩的也恰到好處记餐。比方說,對(duì)于root layer來說薇正,添加動(dòng)畫一般會(huì)調(diào)用UIViewAnimationWithBlocks這個(gè)category中的animateWithDuration系列方法, 使用者只需要在callback中寫入想執(zhí)行動(dòng)畫的代碼即可.類似spring這樣的具有damping效果的動(dòng)畫也可以這樣添加片酝。
但是,這些都是較為簡(jiǎn)單的動(dòng)畫挖腰,而且針對(duì)的是root layer雕沿。那如果我們的layer是我們自己添加的sublayer該怎么辦呢?答案也很簡(jiǎn)單猴仑,添加顯式動(dòng)畫审轮。之前提供的那個(gè)<a >中文版</a>鏈接中有不同layer添加動(dòng)畫的demo code, 包括設(shè)置各種不同屬性等等,大家可以參考。我在這里更多的是探討下Animation的結(jié)構(gòu).
顯式動(dòng)畫的基類為CAAnimation,常用的是CABasicAnimation, CAKeyframeAnimation有時(shí)候還會(huì)使用到CAAnimationGroup來做組合動(dòng)畫. 不過我們這次只講比較重要的CABasicAnimation和CAKeyframeAnimation疾渣,其他的有機(jī)會(huì)再講篡诽。
不管是CABasicAnimation還是CAKeyframeAnimation都是繼承于CAPropertyAnimation.
圖中有3個(gè)屬性, fromValue, toValue和interpolated point.這三個(gè)都是可選的, 但是不得同時(shí)多于2個(gè)為非空,一般來說榴捡,我們僅指定起始點(diǎn)和終點(diǎn)即可杈女,中間的值由timing function插值計(jì)算出來, 默認(rèn)是線性變換. Timing function是控制插值計(jì)算的,形象點(diǎn)說是Timing Function決定了動(dòng)畫運(yùn)行的節(jié)奏(Pacing), 比如是均勻變化(相同時(shí)間變化量相同),先快后慢,先慢后快還是先慢再快再慢. 例如我們?cè)诮oview添加動(dòng)畫時(shí)吊圾,會(huì)指定一些option例如 curveIn, curverOut, flip等等达椰,這些其實(shí)實(shí)現(xiàn)時(shí)都是依靠時(shí)間函數(shù)來實(shí)現(xiàn)。時(shí)間函數(shù)本質(zhì)是映射项乒,決定了時(shí)間和對(duì)應(yīng)的變化量砰碴,例如一秒內(nèi)透明度的變化. 當(dāng)使用basicAnimation時(shí), 自定義的timing function可以是一條光滑的貝塞爾曲線, 由起點(diǎn),終點(diǎn)和2個(gè)關(guān)鍵點(diǎn)控制: 如下圖:
但是如果動(dòng)畫比較復(fù)雜, 既不是直線板丽,也不是貝塞爾曲線, 那么就需要通過CAKeyframeAnimation來實(shí)現(xiàn). 關(guān)鍵幀動(dòng)畫很強(qiáng)大,可以讓我們充分的實(shí)現(xiàn)我們自定義的動(dòng)畫效果趁尼,例如落葉的飄落埃碱,縱向翻轉(zhuǎn)(轉(zhuǎn)場(chǎng)動(dòng)畫)等等。當(dāng)然你可以用嵌套basicAnimation的方式實(shí)現(xiàn)(平移+旋轉(zhuǎn))酥泞,但是那樣既復(fù)雜砚殿,代碼不簡(jiǎn)潔,而且動(dòng)畫很生硬芝囤。但Apple給了我們解決方案---keyFrame Animation!
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
options:(UIViewKeyframeAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^__nullable)(BOOL finished))completion
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime
relativeDuration:(double)frameDuration
animations:(void (^)(void))animations
- frameStartTime 表示關(guān)鍵幀動(dòng)畫開始的時(shí)刻在整個(gè)動(dòng)畫中的百分比
- frameDuration 表示這個(gè)關(guān)鍵幀動(dòng)畫占用整個(gè)動(dòng)畫時(shí)長的百分比
我們需要做的就是在第一個(gè)方法中添加關(guān)鍵幀, 設(shè)定開始時(shí)間和持續(xù)時(shí)間, 并在animation callback中設(shè)置某個(gè)keyFrame結(jié)束時(shí)的view位置似炎,透明度等等, 之后,系統(tǒng)會(huì)為我們插入中間幀, 確保動(dòng)畫的平滑悯姊。在關(guān)鍵幀動(dòng)畫中還有一個(gè)非常重要的參數(shù),那便是calculationMode,計(jì)算模式. 他決定插值的方式: 即當(dāng)在平面座標(biāo)系中有多個(gè)離散的點(diǎn)的時(shí)候,可以是離散的,也可以直線相連后進(jìn)行插值計(jì)算,也可以使用圓滑的曲線將他們相連后進(jìn)行插值計(jì)算:
- UIViewKeyframeAnimationOptionCalculationModeLinear // 連續(xù)運(yùn)算模式羡藐,線性
- UIViewKeyframeAnimationOptionCalculationModeDiscrete // 離散運(yùn)算模式,只顯示關(guān)鍵幀
- UIViewKeyframeAnimationOptionCalculationModePaced // 均勻執(zhí)行運(yùn)算模式悯许,線性
- UIViewKeyframeAnimationOptionCalculationModeCubic // 平滑運(yùn)算模式
- UIViewKeyframeAnimationOptionCalculationModeCubicPaced // 平滑均勻運(yùn)算模式
總結(jié)
Apple的CoreAnimation由兩部分組成: CALayer和Animation. 它們兩個(gè)分別確保了view的顯示和動(dòng)畫的平滑仆嗦。深刻理解core animation的原理不僅有助于我們調(diào)試代碼, 理解他們之間的關(guān)系,更可以幫助我們創(chuàng)造出優(yōu)美且性能優(yōu)越的動(dòng)畫, 提升用戶體驗(yàn).