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关于时间的处理

相关文章
相关标签/搜索