最近学习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是怎么算的?动画
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动画的时序的计算是理解以上代码最关键的一点。在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