iOS動畫暫停與恢復的理解

最近學習iOS動畫相關的知識,學習到控制動畫的暫定與恢復的時候,對其中的timeOffset,beginTime,fillMode等概念不太理解,遂查閱資料,學習一個。html

官方文檔中給出的暫停與恢復layer動畫的代碼以下:
ios

-(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;
}複製代碼

懵逼點: 

1. CACurrentMediaTime()是啥?bash

2. layer對象調用的convertTime: fromLayer:是啥意思?爲何要這樣作?  app

3. timeOffset是啥?暫停動畫爲何要設置這個?  ide

4. beginTime是啥?爲何要設置兩次beginTime?第一次設置爲0,第二次設置爲timeSincePause? 學習

 5. timeSincePause是怎麼算的?動畫


iOS的時間

mach_absolute_time()ui

mach_absolute_time()可能用到的同窗比較少,但這個概念很是重要。  spa

描述絕對時間須要找到一個均勻變化的屬性值來描述時間,CPU的時鐘週期數(ticks)恰好就是這樣的一個屬性。這個tick的數值能夠用來描述時間,而mach_absolute_time()返回的就是CPU已經運行的tick的數量。將這個tick數通過必定的轉換就能夠變成秒數,或者納秒數,這樣就和時間直接關聯了。  code

不過這個tick數,在每次手機重啓以後,會從新開始計數,並且iPhone鎖屏進入休眠以後tick也會暫停計數。 

 mach_absolute_time()不會受系統時間影響,只受設備重啓和休眠行爲影響。

CACurrentMediaTime()

CACurrentMediaTime()可能接觸到的同窗會多一些,先看下官方的定義:

/* Returns the current CoreAnimation absolute time. This is the result of
 * calling mach_absolute_time () and converting the units to seconds. */
CFTimeInterval CACurrentMediaTime (void)複製代碼

CACurrentMediaTime()就是將上面mach_absolute_time()的CPU tick數轉化成秒數的結果。如下代碼: 

double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);複製代碼

返回的就是開機後設備一共運行了(設備休眠不統計在內)多少秒,另外一個API也能返回相同的值:

NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
NSLog(@"systemUptime: %f", systemUptime);複製代碼

CACurrentMediaTime()也不會受系統時間影響,只受設備重啓和休眠行爲影響。


iOS動畫的時序

iOS動畫的時序的計算是理解以上代碼最關鍵的一點。在CAMediaTiming協議的timeOffset的註釋上,官方給出一下公式:

t = (tp - begin) * speed + offset

t的是就是須要計算的動畫的時間點

tp是父layer的時間點,爲了方便理解,能夠認爲是絕對時間,隨時間流逝而增長。

begin、speed、offset就是動畫的屬性beginTime、speed、timeOffset。

下面根據以上公式,來嘗試暫停和恢復動畫


動畫的暫停

默認狀況下,speed等於1,begin等於0,offset等於0。帶入等式,獲得

t = tp

若是想要暫停動畫,毫無疑問,須要將speed設置爲0。

可是若是speed等於0,上面的等式就不成立了。將speed等於0帶入上面的等式,獲得

t = offset

而offset默認等於0,動畫就回到了最初的位置了!所以,須要將暫停時的 t 的值,賦值給offset,這樣,t 的值就能夠維持在暫停的時候了。因此設置 offset = pausedTime

-(void)pauseLayer:(CALayer*)layer {
   CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
   layer.speed = 0.0;
   layer.timeOffset = pausedTime;
}複製代碼

動畫的恢復

要想恢復動畫,須要作兩點:

1. 將速度調整爲1,同時將offset恢復成0,否則下次暫停時,offset就不對了。

2. t 的值要等於上面暫停時的值。由於動畫要從暫停的時候繼續往下播放

t =(tp-begin)*speed + offset

暫停的時間點仍是上面算出來的pausedTime,因此須要構造等式,使得在speed等於1,offset等於0的狀況下,使得等式左邊的 t 等於暫停時算出來的timeOffset。即

t = tp-begin = pausedTime

因此 begin 要等於 tp - 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;
}複製代碼

那麼,爲何要先將beginTime設置爲0,再將 beginTime 設置爲 tp - pausedTime 呢?

由於 convertTime:fromLayer: 在計算當前layer的時間時,會使用到 layer 的beginTime,

self.nicoLayer.beginTime = 0;
    NSLog(@"beginTime1 = %f", self.nicoLayer.beginTime);
    CFTimeInterval tp1 = [self.nicoLayer convertTime:CACurrentMediaTime() fromLayer:nil];
    NSLog(@"tp1 = %f", tp1);
    
    self.nicoLayer.beginTime = 10;
    NSLog(@"beginTime2 = %f", self.nicoLayer.beginTime);
    CFTimeInterval tp2 = [self.nicoLayer convertTime:CACurrentMediaTime() fromLayer:nil];
    NSLog(@"tp2 = %f", tp2);複製代碼

打印結果:

beginTime1 = 0.000000
    tp1 = 81431.807847
    beginTime2 = 10.000000
    tp2 = 81421.808063複製代碼

因而可知,convertTime:fromLayer: 方法在計算的時候,會使用到上面的公式,因此須要將beginTime先恢復成0,再進行計算,才能獲得正確的時間。


簡單理解:

1. beginTime爲正的時候,計算動畫時間的時候,時間點會往前位移beginTime秒

2. timeOffset爲正的時候,計算動畫時間的時候,時間點會日後位移timeOffset秒


懵逼點理解:

1. CACurrentMediaTime()是啥?

答:CACurrentMediaTime()就是將上面mach_absolute_time()的CPU tick數轉化成秒數的結果。CACurrentMediaTime()也不會受系統時間影響,只受設備重啓和休眠行爲影響。

2. layer對象調用的convertTime: fromLayer:是啥意思?爲何要這樣作?

答:計算當前layer的絕對時間

3. timeOffset是啥?暫停動畫爲何要設置這個?

答:動畫時間點的計算知足一個公式,t = (tp - begin) * speed + offset,設置timeOffset使得公式在speed等於0的狀況下,動畫的時間點(或位置)等於暫停時的時間(或位置)

4. beginTime是啥?爲何要設置兩次beginTime?第一次設置爲0,第二次設置爲timeSincePause?

答:同上,是爲了在speed設置爲1的狀況下,使等式成立。

5. timeSincePause是怎麼算的?

答:tp是當前時間點,pausedTime是暫停時的時間點,pausedTime是固定不變的,tp隨時間流逝而增長,因此timeSincePause是從暫停到當前時間的間隔時長,beginTime設置爲timeSincePause,即將動畫向前位移到上次停下來的時間點。


參考

Apple Document: Pausing and Resuming Animations

Stack Overflow: Comprehend pause and resume animation on a layer

MrPeak: iOS關於時間的處理

相關文章
相關標籤/搜索