CALayer
和CAAnimation
都实现了CAMediaTiming
协议,可以通过CALayer
中实现的协议中的属性来控制动画。
停止动画:通过将speed设置为0,并将timeOffset调整到合适的值。
Apple 文档说明
/* The rate of the layer. Used to scale parent time to local time, e.g. if rate is 2, local time progresses twice as fast as parent time. Defaults to 1. */
@property float speed;
/* Additional offset in active local time. i.e. to convert from parent time tp to active local time t: t = (tp - begin) * speed + offset.
One use of this is to "pause" a layer by setting `speed' to zero and `offset' to a suitable value. Defaults to 0. */
@property CFTimeInterval timeOffset;
而恢复动画,除了上面两个属性,还需要配合beginTime
属性来让动画在正确的时间、位置恢复
##方法一
// 停止
- (void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
// 让CALayer的时间停止走动
layer.speed = 0.0;
// 让CALayer的时间停留在pausedTime这个时刻
layer.timeOffset = pausedTime;
}
恢复动画:
- (void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = layer.timeOffset;
// 1. 让CALayer的时间继续行走
layer.speed = 1.0;
// 2. 取消上次记录的停留时刻
layer.timeOffset = 0.0;
// 3. 取消上次设置的时间
layer.beginTime = 0.0; // 可以放在pauseLayer:方法中方便理解
// 4. 计算暂停的时间(这里也可以用CACurrentMediaTime()-pausedTime)
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
// 5. 设置相对于父坐标系的开始时间(往后退timeSincePause)
layer.beginTime = timeSincePause;
}
以上方法中有几点较为难以理解的地方,待后续解读。
1 . CALayer
中beginTime的含义。CAAnimation
的beginTime表现为从duration
的哪个点开始动画。如果不做停止/恢复动画的动作,给CALayer
设置一个动画,并设置它的beginTime
属性,结果对动画的表现并没有任何影响。
疑点1解答:beginTime须设置为与层时间相关的值,
如CACurrentMediaTime() + 1, 将会延迟1s开始动画;
而不能直接设置为 1s。
另外beginTime可以在CAGroupAnimation中控制某个动画在组动画中的起始时间。
2 . 恢复动画时,先设置layer.beginTime = 0
随后马上又设置layer.beginTime = timeSincePause
。如果少了第一句(layer.beginTime = 0
),在动画第二次恢复时,不会衔接暂停时的位置,而是取决于你暂停了多少时间,这段时间内layer
本应该动画到哪个位置,它就会从那个位置恢复动画。但对第一次恢复没有影响。
第2点解答:恢复动画时,先设`layer.beginTime = 0.0` 是为了后一句代码`[layer convertTime:CACurrentMediaTime() fromLayer:nil]` 能得到正确的层上的时间,如果没有layer.beginTime = 0.0, 则convert出的时间将会是上次停止动画的时间pausedTime;layer.beginTime = 0.0 这句代码可以放在停止动画方法中去。
3 . 其中,beginTime
、timeOffset
于CACurrentMediaTime()
之间的关系。
方法二
// 停止动画
- (void)pauseAnimation
{
CFTimeInterval current = CACurrentMediaTime();
CFTimeInterval pauseTime = current - layer.beginTime;
/* 实际此处
[layer convertTime:CACurrentMediaTime() fromLayer:nil] == current - layer.beginTime == pauseTime
*/
layer.speed = 0;
layer.timeOffset = pauseTime;
layer.beginTime = 0.;
}
- (void)resumeAnimation
{
CFTimeInterval pauseTime = layer.timeOffset;
CFTimeInterval timeSincePause = CACurrentMediaTime() - pauseTime;
/*
[layer convertTime:CACurrentMediaTime() fromLayer:nil] == pauseTime
*/
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = timeSincePause;
}
timeSincePause 是动画停止了多长时间,动画停止了而层上的时间CACurrentMediaTime()
仍然在往前走,因此需要将layer.beginTime
往回退,才能让动画在正确的时间位置恢复。