图层时间
我们可以用CAAnimation
和它的子类实现的多种图层动画。但是动画是需要持续时间的,因此我们来学习一下计时的CAMediaTiming
,跟踪时间。
CAMediaTiming 协议
CAMediaTiming
协议定义了在一段动画内用来控制逝去时间的属性的集合。CALayer
和CAAnimation
都实现了这个协议,所以时间可以被任意基于一个图层或者一段动画的类控制。
持续和重复
duration
(CAMediaTiming
的属性之一)是CFTimeInterval
类型(双精度浮点型),对将要进行的动画的一次迭代指定了时间。
repeatCount
(CAMediaTiming
的属性之一),代表动画重复的迭代次数。
通过上面两个属性我们可以创建重复动画
创建重复动画的另外方式:
repeatDuration
属性。它让动画重复一个指定的时间,而不是指定次数。如果我们将 repeatDuration
设置为INFINITY
(无限大)动画无限循环播放,设置repeatCount
为INFINITY
也有同样的效果。(但是这两个属性我们只能设置一个)
autoreverses
属性,设置是否在每次间隔交替循环过程中自动回放(常用于播放非循环动画)。
相对时间
beginTime
指定了动画开始之前的的延迟时间(默认是0即立刻执行)。
设置beginTime属性为:要延迟的时间(单位为秒)+ CACurrentMediaTime();
speed
是一个时间的倍数,默认1.0
,减少它会减慢图层/动画的时间,增加它会加快速度。如果2.0的速度,那么对于一个 duration 为1的动画,实际上在0.5秒的时候就已经完成了。
timeOffset
和beginTime
类似,但是和增加beginTime
导致的延迟动画不同,增加timeOffset
只是让动画快进到某一点,例如,对于一个持续1秒的动画来说,设置timeOffset
为0.5意味着动画将从一半的地方开始。
和beginTime
不同的是,timeOffset
并不受 speed
的影响。所以如果你把speed
设为2.0,把 timeOffset
设置为0.5,那么你的动画将从动画最后结束的地方开始,因为1秒的动画实际上被缩短到了0.5秒。然而即使使用了 timeOffset
让动画从结束的地方开始,它仍然播放了一个完整的时长,这个动画仅仅是循环了一圈,然后从头开始播放。
fillMode
fillMode
属性是一个NSString
类型,接受以下常量:
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
kCAFillModeRemoved//默认
默认是 kCAFillModeRemoved
,当动画不再播放的时候就显示图层模型指定的值
向前,向后或者即向前又向后去填充动画状态,使得动画在开始前或者结束后仍然保持开始和结束那一刻的值。
这就对避免在动画结束的时候急速返回提供另一种方案。但是,需要把 removeOnCompletion 设置为 NO ,另外需要给动画添加一个非空的键,于是可以在不需要动画的时候把它从图层上移除.
层级关系时间
动画时间也是有层级关系的,每个动画和图层在时间上都有它自己的层级概念,相对于父图层.对图层调整时间将会影响到它本身和子图层的动画,但不会影响到父图层(图层的时间属性)。类似的使用CAAnimationGroup
时将动画都被按照层级组合.(CAAnimationGroup将动画层级组合)(动画属性)
对CALayer
或者 CAGroupAnimation
调整 duration
和 repeatCount / repeatDuration
属性并不会影响到子动画。但是 beginTime
, timeOffset
和 speed
属性将会影响到子动画。beginTime
指定了父图层开始动画(或者组合关系中的父动画)和对象将要开始自己动画之间的偏移。调整CALayer
和CAGroupAnimation
的 speed
属性将会对动画以及子动画速度应用一个缩放的因子。
全局时间和本地时间
全局时间:
CoreAnimation有一个全局时间的概念,也就是所谓的马赫时间(“马赫”实际上是iOS和Mac OS系统内核的命名)(返回了设备自从上次启动后的秒数)。马赫时间在设备上所有进程都是全局的--但是在不同设备上并不是全局的.
//CACurrentMediaTime 函数来访问马赫时间
CFTimeInterval time = CACurrentMediaTime();
这个值我们并不关心,但是它对我们的对时间实测提供了一个基准,可以提供相对值.
注意
当设备休眠的时候马赫时间会暂停,也就是所有的CAAnimations
(基于马赫时间)同样也会暂停。因此马赫时间对长时间测量并不有用。比如用 CACurrentMediaTime 去更新一个实时闹钟并不明智。
本地时间:
每个 CALayer 和 CAAnimation 实例都有自己本地时间的概念,是根据父图层/动画层级关系中的 beginTime , timeOffset 和 speed 属性计算出来的.
类似坐标系转换:
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)layer;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)layer;
暂停,倒回和快进
设置动画的speed
属性为0可以暂停动画,但在动画被添加到图层之后不太可能再修改它了,所以不能对正在进行的动画使用这个属性(注意这是动画的speed属性)。
如果移除图层正在进行的动画,图层将会急速返回动画之前的状态。但如果在动画移除之前拷贝呈现图层到模型图层,动画将会看起来暂停在那里。但是不好的地方在于不能再恢复动画了。
给图层添加一个CAAnimation
实际上是给动画对象做了一个不可改变的拷贝,所以对原始动画对象属性的改变对真实的动画并没有作用。相反,直接用 - animationForKey:
来检索图层正在进行的动画可以返回正确的动画对象,但是修改它的属性将会抛出异常。
暂停的方法:
可以利用CAMediaTiming
来暂停图层本身。如果把图层的 speed
设置成0,它会暂停任何添加到图层上的动画。类似的,设置speed
大于1.0将会快进,设置成一个负值将会倒回动画。
通过增加主窗口图层的speed,可以暂停整个应用程序的动画.我们可以利用改变speed来加速所有视图动画来进行自动化测试.
self.window.layer.speed = 100;
手动动画
我们可以通过设置动画speed
属性为0,禁用动画的自动播放,然后通过设置timeOffset
来显示动画序列.这样就可以运用手势来控制动画.
例如:
//设置平移手势
- (void)pan:(UIPanGestureRecognizer *)pan
{
//get horizontal component of pan gesture
CGFloat x = [pan translationInView:self.view].x;
//convert from points to animation duration //using a reasonable scale factor
x /= 200.0f;
//update timeOffset and clamp result
CFTimeInterval timeOffset = self.doorLayer.timeOffset;
timeOffset = MIN(0.999, MAX(0.0, timeOffset - x));
self.doorLayer.timeOffset = timeOffset;
//reset pan gesture
[pan setTranslation:CGPointZero inView:self.view];
}
其实如果是这样的话,对于比较复杂的动画我们可以这样的方法,但是对于比较简单图层我们完全可以直接用移动手势来设置图层transform
,
w