CALayer 以及時間模型<轉>


轉自http://geeklu.com/2012/09/animation-in-ios/

二.CALayer及時間模型

我們都知道UIView是MVC中的View.UIView的職責在於界面的顯示和界面事件的處理.每一個View的背后都有一個layer(可以通過view.layer進行訪問),layer是用於界面顯示的.CALayer屬於QuartzCore框架,非常重要,但並沒有想象中的那么好理解.我們通常操作的用於顯示的Layer在Core Animation這層的概念中其實擔當的是數據模型Model的角色,它並不直接做渲染的工作.關於Layer,之前從座標系的角度分析過,這次則側重於它的時間系統.

1.Layer的渲染架構

Layer也和View一樣存在着一個層級樹狀結構,稱之為圖層樹(Layer Tree),直接創建的或者通過UIView獲得的(view.layer)用於顯示的圖層樹,稱之為模型樹(Model Tree),模型樹的背后還存在兩份圖層樹的拷貝,一個是呈現樹(Presentation Tree),一個是渲染樹(Render Tree). 呈現樹可以通過普通layer(其實就是模型樹)的layer.presentationLayer獲得,而模型樹則可以通過modelLayer屬性獲得(詳情文檔).模型樹的屬性在其被修改的時候就變成了新的值,這個是可以用代碼直接操控的部分;呈現樹的屬性值和動畫運行過程中界面上看到的是一致的.而渲染樹是私有的,你無法訪問到,渲染樹是對呈現樹的數據進行渲染,為了不阻塞主線程,渲染的過程是在單獨的進程或線程中進行的,所以你會發現Animation的動畫並不會阻塞主線程.

2.事務管理

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];

所有的非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會被調用.

3.時間系統

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協議中幾個重要的屬性.

beginTime

無論是圖層還是動畫,都有一個時間線Timeline的概念,他們的beginTime是相對於父級對象的開始時間. 雖然蘋果的文檔中沒有指明,但是通過代碼測試可以發現,默認情況下所有的CALayer圖層的時間線都是一致的,他們的beginTime都是0,絕對時間轉換到當前Layer中的時間大小就是絕對時間的大小.所以對於圖層而言,雖然創建有先后,但是他們的時間線都是一致的(只要不主動去修改某個圖層的beginTime),所以我們可以想象成所有的圖層默認都是從系統重啟后開始了他們的時間線的計時.

但是動畫的時間線的情況就不同了,當一個動畫創建好,被加入到某個Layer的時候,會先被拷貝一份出來用於加入當前的圖層,在CA事務被提交的時候,如果圖層中的動畫的beginTime為0,則beginTime會被設定為當前圖層的當前時間,使得動畫立即開始.如果你想某個直接加入圖層的動畫稍后執行,可以通過手動設置這個動畫的beginTime,但需要注意的是這個beginTime需要為 CACurrentMediaTime()+延遲的秒數,因為beginTime是指其父級對象的時間線上的某個時間,這個時候動畫的父級對象為加入的這個圖層,圖層當前的時間其實為[layer convertTime:CACurrentMediaTime() fromLayer:nil],其實就等於CACurrentMediaTime(),那么再在這個layer的時間線上往后延遲一定的秒數便得到上面的那個結果.

timeOffset

這個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

speed屬性用於設置當前對象的時間流相對於父級對象時間流的流逝速度,比如一個動畫beginTime是0,但是speed是2,那么這個動畫的1秒處相當於父級對象時間流中的2秒處. speed越大則說明時間流逝速度越快,那動畫也就越快.比如一個speed為2的layer其所有的父輩的speed都是1,它有一個subLayer,speed也為2,那么一個8秒的動畫在這個運行於這個subLayer只需2秒(8 / (2 * 2)).所以speed有疊加的效果.

fillMode

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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM