轉:http://geeklu.com/2012/09/animation-in-ios/
html
零.前言ios
這裏沒有太多的代碼細節,只是探索iOS動畫的基本概念,以及其抽象模型,數學基礎等.咱們學習一個知識的時候通常有兩個部分,抽象部分和形象部分,抽象比如語言的語法,是規則,形象比如具體的句子,能夠用來和別人交流的.抽象比形象難於理解,但比形象通用.其實數學中常常碰到抽象和形象的概念,好比有一系列離散的點,這是形象;經過這些點咱們擬合出一條曲線,獲得其函數,函數是抽象的;而後經過這個函數咱們能夠獲得更多的點,這又回到了形象上.因此學習任何知識不能僅僅停留在會用了,而要上升一個層次,去學習研究其抽象層次上的知識,抽象層度越高,則越通用.架構
什麼是Animation(動畫),簡單點說就是在一段時間內,顯示的內容發生了變化.對CALayer來講就是在一段時間內,其Animatable Property發生了變化.從CALayer(CA = Core Animation)類名來看就能夠看出iOS的Layer就是爲動畫而生的,便於實現良好的交互體驗. 這裏涉及到兩個東西: 一是Layer(基類CALayer),一是Animation(基於CAAnimation). Animation做用於Layer.CALayer提供了接口用於給本身添加Animation. 用於顯示的Layer本質上講是一個Model,包含了Layer的各類屬性值. Animation則包含了動畫的時間,變化,以及變化的速度.下面分別詳細講解Layer和Animation相關知識.app
咱們都知道UIView是MVC中的View.UIView的職責在於界面的顯示和界面事件的處理.每個View的背後都有一個layer(能夠經過view.layer進行訪問),layer是用於界面顯示的.CALayer屬於QuartzCore框架,很是重要,但並無想象中的那麼好理解.咱們一般操做的用於顯示的Layer在Core Animation這層的概念中其實擔當的是數據模型Model的角色,它並不直接作渲染的工做.關於Layer,以前從座標系的角度分析過,此次則側重於它的時間系統.框架
Layer也和View同樣存在着一個層級樹狀結構,稱之爲圖層樹(Layer Tree),直接建立的或者經過UIView得到的(view.layer)用於顯示的圖層樹,稱之爲模型樹(Model Tree),模型樹的背後還存在兩份圖層樹的拷貝,一個是呈現樹(Presentation Tree),一個是渲染樹(Render Tree). 呈現樹能夠經過普通layer(其實就是模型樹)的layer.presentationLayer得到,而模型樹則能夠經過modelLayer屬性得到(詳情文檔).模型樹的屬性在其被修改的時候就變成了新的值,這個是能夠用代碼直接操控的部分;呈現樹的屬性值和動畫運行過程當中界面上看到的是一致的.而渲染樹是私有的,你沒法訪問到,渲染樹是對呈現樹的數據進行渲染,爲了避免阻塞主線程,渲染的過程是在單獨的進程或線程中進行的,因此你會發現Animation的動畫並不會阻塞主線程.ide
CALayer的那些可用於動畫的(Animatable)屬性,稱之爲Animatable Properties,這裏有一份詳情的列表,羅列了全部的 CALayer Animatable Properties. 若是一個Layer對象存在對應着的View,則稱這個Layer是一個Root Layer,非Root Layer通常都是經過CALayer或其子類直接建立的.下面的subLayer就是一個典型的非Root Layer,它沒有對應的View對象關聯着.函數
subLayer = [[CALayer alloc] init]; subLayer.frame = CGRectMake(0, 0, 300, 300); subLayer.backgroundColor= [[UIColor redColor] CGColor]; [self.view.layer addSublayer:subLayer];
oop
全部的非Root Layer在設置Animatable Properties的時候都存在着隱式動畫,默認的duration是0.25秒.性能
subLayer.position = CGPointMake(300,400);
學習
像上面這段代碼當下一個RunLoop開始的時候並非直接將subLayer的position變成(300,400)的,而是有個移動的動畫進行過渡完成的.
任何Layer的animatable屬性的設置都應該屬於某個CA事務(CATransaction),事務的做用是爲了保證多個animatable屬性的變化同時進行,不論是同一個layer仍是不一樣的layer之間的.CATransaction也分兩類,顯式的和隱式的,當在某次RunLoop中設置一個animatable屬性的時候,若是發現當前沒有事務,則會自動建立一個CA事務,在線程的下個RunLoop開始時自動commit這個事務,若是在沒有RunLoop的地方設置layer的animatable屬性,則必須使用顯式的事務.
顯式事務的使用以下:
[CATransaction begin];
...
[CATransaction commit];
事務能夠嵌套.當事務嵌套時候,只有當最外層的事務commit了以後,整個動畫纔開始.
能夠經過CATransaction來設置一個事務級別的動畫屬性,覆蓋隱式動畫的相關屬性,好比覆蓋隱式動畫的duration,timingFunction.若是是顯式動畫沒有設置duration或者timingFunction,那麼CA事務設置的這些參數也會對這個顯式動畫起做用.
還能夠設置completionBlock,噹噹前CATransaction的全部動畫執行結束後, completionBlock會被調用.
CALayer實現了CAMediaTiming協議. CALayer經過CAMediaTiming協議實現了一個有層級關係的時間系統.除了CALayer,CAAnimation也採納了此協議,用來實現動畫的時間系統.
在CA中,有一個Absolute Time(絕對時間)的概念,能夠經過CACurrentMediaTime()得到,其實這個絕對時間就是將mach_absolute_time()轉換成秒後的值.這個時間和系統的uptime有關,系統重啓後CACurrentMediaTime()會被重置.
就和座標存在相對座標同樣,不一樣的實現了CAMediaTiming協議的存在層級關係的對象也存在相對時間,常常須要進行時間的轉換,CALayer提供了兩個時間轉換的方法:
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
如今來重點研究CAMediaTiming協議中幾個重要的屬性.
不管是圖層仍是動畫,都有一個時間線Timeline的概念,他們的beginTime是相對於父級對象的開始時間. 雖然蘋果的文檔中沒有指明,可是經過代碼測試能夠發現,默認狀況下全部的CALayer圖層的時間線都是一致的,他們的beginTime都是0,絕對時間轉換到當前Layer中的時間大小就是絕對時間的大小.因此對於圖層而言,雖然建立有前後,可是他們的時間線都是一致的(只要不主動去修改某個圖層的beginTime),因此咱們能夠想象成全部的圖層默認都是從系統重啓後開始了他們的時間線的計時.
可是動畫的時間線的狀況就不一樣了,當一個動畫建立好,被加入到某個Layer的時候,會先被拷貝一份出來用於加入當前的圖層,在CA事務被提交的時候,若是圖層中的動畫的beginTime爲0,則beginTime會被設定爲當前圖層的當前時間,使得動畫當即開始.若是你想某個直接加入圖層的動畫稍後執行,能夠經過手動設置這個動畫的beginTime,但須要注意的是這個beginTime須要爲 CACurrentMediaTime()+延遲的秒數,由於beginTime是指其父級對象的時間線上的某個時間,這個時候動畫的父級對象爲加入的這個圖層,圖層當前的時間其實爲[layer convertTime:CACurrentMediaTime() fromLayer:nil],其實就等於CACurrentMediaTime(),那麼再在這個layer的時間線上日後延遲必定的秒數便獲得上面的那個結果.
這個timeOffset多是這幾個屬性中比較難理解的一個,官方的文檔也沒有講的很清楚. local time也分紅兩種一種是active local time 一種是basic local time.
timeOffset則是active local time的偏移量.
你將一個動畫看做一個環,timeOffset改變的實際上是動畫在環內的起點,好比一個duration爲5秒的動畫,將timeOffset設置爲2(或者7,模5爲2),那麼動畫的運行則是從原來的2秒開始到5秒,接着再0秒到2秒,完成一次動畫.
speed屬性用於設置當前對象的時間流相對於父級對象時間流的流逝速度,好比一個動畫beginTime是0,可是speed是2,那麼這個動畫的1秒處至關於父級對象時間流中的2秒處. speed越大則說明時間流逝速度越快,那動畫也就越快.好比一個speed爲2的layer其全部的父輩的speed都是1,它有一個subLayer,speed也爲2,那麼一個8秒的動畫在這個運行於這個subLayer只需2秒(8 / (2 * 2)).因此speed有疊加的效果.
fillMode的做用就是決定當前對象過了非active時間段的行爲. 好比動畫開始以前,動畫結束以後。若是是一個動畫CAAnimation,則須要將其removedOnCompletion設置爲NO,要否則fillMode不起做用. 下面來說各個fillMode的意義
kCAFillModeRemoved 這個是默認值,也就是說當動畫開始前和動畫結束後,動畫對layer都沒有影響,動畫結束後,layer會恢復到以前的狀態
kCAFillModeForwards 當動畫結束後,layer會一直保持着動畫最後的狀態
kCAFillModeBackwards 這個和kCAFillModeForwards是相對的,就是在動畫開始前,你只要將動畫加入了一個layer,layer便當即進入動畫的初始狀態並等待動畫開始.你能夠這樣設定測試代碼,將一個動畫加入一個layer的時候延遲5秒執行.而後就會發如今動畫沒有開始的時候,只要動畫被加入了layer,layer便處於動畫初始狀態
kCAFillModeBoth 理解了上面兩個,這個就很好理解了,這個其實就是上面兩個的合成.動畫加入後開始以前,layer便處於動畫初始狀態,動畫結束後layer保持動畫最後的狀態.
其餘的一些參數都是比較容易理解的.
參見蘋果官方 QA1673 How to pause the animation of a layer tree
-(void)pauseLayer:(CALayer*)layer{ CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime()fromLayer:nil]; layer.speed = 0.0; layer.timeOffset = pausedTime;}-(void)resumeLayer:(CALayer*)layer{CFTimeInterval pausedTime = [layer timeOffset]; layer.speed = 1.0; layer.timeOffset = 0.0; layer.beginTime= 0.0; CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] -pausedTime; layer.beginTime = timeSincePause;}
當須要對非Root Layer進行動畫或者須要對動畫作更多自定義的行爲的時候,就必須使用到顯式動畫了,顯式動畫的基類爲CAAnimation,經常使用的是CABasicAnimation,CAKeyframeAnimation有時候還會使用到CAAnimationGroup,CATransition(注意不是CATransaction,Transition是過渡的意思).
這裏再強調關於動畫的兩個重要的點:一是中間狀態的插值計算(Interpolation),二是動畫節奏控制(Timing); 有時候插值計算也和Timing有必定關係. 若是狀態是一維空間的值(好比透明度),那麼插值計算的結果必然再起點值和終點值之間,若是狀態是二維空間的值(好比position),那麼通常狀況下插值獲得的點會落在起點和終點之間的線段上(固然也有可能連線是圓滑曲線).
不論是CABasicAnimation仍是CAKeyframeAnimation都是繼承於CAPropertyAnimation.
CABasicAnimation有三個比較重要的屬性,fromValue,toValue,byValue,這三個屬性都是可選的,但不能同時多於兩個爲非空.最終都是爲了肯定animation變化的起點和終點.Setting Interpolation Values詳細介紹了這個三個值的各類狀況以及用途. 設置了動畫的起點和終點以後,中間的值都是經過插值方式計算出來的.插值計算的結果由timingFunction指定,默認timingFunction爲nil,會使用liner的,也就是變化是均勻的.
Timing Function的會被用於變化起點和終點之間的插值計算.形象點說是Timing Function決定了動畫運行的節奏(Pacing),好比是均勻變化(相同時間變化量相同),先快後慢,先慢後快仍是先慢再快再慢.
時間函數是使用的一段函數來描述的,橫座標是時間t取值範圍是0.0-1.0,縱座標是變化量x(t)也是取值範圍也是0.0-1.0 假設有一個動畫,duration是8秒,變化值的起點是a終點是b(假設是透明度),那麼在4秒處的值是多少呢? 能夠經過計算爲 a + x(4/8) * (b-a), 爲何這麼計算呢?講實現的時間映射到單位值的時候4秒相對於總時間8秒就是0.5而後能夠獲得0.5的時候單位變化量是 x(0.5), x(0.5)/1 = 實際變化量/(b-a), 其中b-a爲總變化量,因此實際變化量就是x(0.5) * (b-a) ,最後4秒時的值就是 a + x(0.5) * (b-a),因此計算的本質是映射.
Timing Function對應的類是CAMediaTimingFunction,它提供了兩種得到時間函數的方式,一種是使用預約義的五種時間函數,一種是經過給點兩個控制點獲得一個時間函數. 相關的方法爲
+ (id)functionWithName:(NSString *)name;+ (id)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;- (id)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
五種預約義的時間函數名字的常量變量分別爲
kCAMediaTimingFunctionLinear,
kCAMediaTimingFunctionEaseIn,
kCAMediaTimingFunctionEaseOut,
kCAMediaTimingFunctionEaseInEaseOut,
kCAMediaTimingFunctionDefault.
下圖展現了前面四種Timing Function的曲線圖,橫座標表示時間,縱座標表示變化量,這點須要搞清楚(並非平面座標系中xy).
自定義的Timing Function的函數圖像就是一條三次貝塞爾曲線Cubic Bezier Curve,貝塞爾曲線的優勢就是光滑,用在這裏就使得變化顯得光滑.一條三次貝塞爾曲線能夠由起點終點以及兩個控制點決定.
上面的kCAMediaTimingFunctionDefault對應的函數曲線其實就是經過[(0.0,0.0), (0.25,0.1), (0.25,0.1), (1.0,1.0)]這四個點決定的三次貝塞爾曲線,頭尾爲起點和終點,中間的兩個點是控制點.
上圖中P0是起點,P3是終點,P1和P2是兩個控制點
若是時間變化曲線既不是直線也不是貝塞爾曲線,而是自定義的,又或者某個圖層運動的軌跡不是直線而是一個曲線,這些是基本動畫沒法作到的,因此引入下面的內容,CAKeyframeAnimation,也即所謂的關鍵幀動畫.
任何動畫要表現出運動或者變化,至少須要兩個不一樣的關鍵狀態,而中間的狀態的變化能夠經過插值計算完成,從而造成補間動畫,表示關鍵狀態的幀叫作關鍵幀. CABasicAnimation其實能夠看做一種特殊的關鍵幀動畫,只有頭尾兩個關鍵幀.CAKeyframeAnimation則能夠支持任意多個關鍵幀,關鍵幀有兩種方式來指定,使用path或者使用values,path是一個CGPathRef的值,且path只能對CALayer的 anchorPoint 和 position 屬性起做用,且設置了path以後values就再也不起效了.而values則更加靈活. keyTimes這個可選參數能夠爲對應的關鍵幀指定對應的時間點,其取值範圍爲0到1.0,keyTimes中的每個時間值都對應values中的每一幀.當keyTimes沒有設置的時候,各個關鍵幀的時間是平分的.
還能夠經過設置可選參數timingFunctions(CAKeyframeAnimation中timingFunction是無效的)爲關鍵幀之間的過渡設置timingFunction,若是values有n個元素,那麼timingFunctions則應該有n-1個.但不少時候並不須要timingFunctions,由於已經設置了夠多的關鍵幀了,好比沒1/60秒就設置了一個關鍵幀,那麼幀率將達到60FPS,徹底不須要相鄰兩幀的過渡效果(固然也有可能某兩幀 值相距較大,可使用均勻變化或者增長幀率,好比每0.01秒設置一個關鍵幀).
在關鍵幀動畫中還有一個很是重要的參數,那即是calculationMode,計算模式.其主要針對的是每一幀的內容爲一個座標點的狀況,也就是對anchorPoint 和 position 進行的動畫.當在平面座標系中有多個離散的點的時候,能夠是離散的,也能夠直線相連後進行插值計算,也可使用圓滑的曲線將他們相連後進行插值計算. calculationMode目前提供以下幾種模式 kCAAnimationLinear
kCAAnimationDiscrete
kCAAnimationPaced
kCAAnimationCubic
kCAAnimationCubicPaced
kCAAnimationLinear calculationMode的默認值,表示當關鍵幀爲座標點的時候,關鍵幀之間直接直線相連進行插值計算;
kCAAnimationDiscrete 離散的,就是不進行插值計算,全部關鍵幀直接逐個進行顯示;
kCAAnimationPaced 使得動畫均勻進行,而不是按keyTimes設置的或者按關鍵幀平分時間,此時keyTimes和timingFunctions無效;
kCAAnimationCubic 對關鍵幀爲座標點的關鍵幀進行圓滑曲線相連後插值計算,對於曲線的形狀還能夠經過tensionValues,continuityValues,biasValues來進行調整自定義,這裏的數學原理是Kochanek–Bartels spline,這裏的主要目的是使得運行的軌跡變得圓滑;
kCAAnimationCubicPaced 看這個名字就知道和kCAAnimationCubic有必定聯繫,其實就是在kCAAnimationCubic的基礎上使得動畫運行變得均勻,就是系統時間內運動的距離相同,此時keyTimes以及timingFunctions也是無效的.
最後推薦下WWDC 2010和2011上的關於Animation相關的Session,你們能夠找找來看.2010的有說到Core Graphic相關內容.以及他們都從性能方面對CA作了些詮釋.