# 在CAAnimation中暂停动画

使用CAAnimation做动画时,如果要移除动画可以使用

```
- (void)removeAnimationForKey:(NSString *)key;
- (void)removeAllAnimations;
```

移除动画后,根据动画 fillmode 的设置,视图会静止在动画起点或者终点的位置,并且动画再次启动还是从起点开始. 如果想让它暂停在当前位置,继续动画时从当前位置开始继续动画,可以根据暂停动画的时间计算暂停位置,然后继续动画时重新制作新的动画.

```
//计算当前视图位置
-(void)resetViewWithPosition:(float)position
{
    CGRect rect  = self.animateView.frame;
    if (self.isVertical)
    {

        rect.origin.y = position;
    }
    else
    {
        rect.origin.x  = position;
    }
    self.animateView.frame = rect;
}

//暂停动画
- (void)nx_pauseAnimate
{
    if (!_isStart || _isPause)
    {
        return;
    }

    _isPause = YES;

    [self.animateView.layer removeAllAnimations];
    //动画已经进行的的时间
    _animateTime = _animateTime + [[NSDate date] timeIntervalSince1970] - _startTime;
    //当前的位置
    float pausePosition = [_animation positionWithPauseTime:_animateTime];
    float scap = (self.isVertical ? CGRectGetHeight(self.animateView.frame) :CGRectGetWidth(self.animateView.frame) )/2.;
    [self resetViewWithPosition:(pausePosition -scap)];
}
//继续动画
- (void)nx_resumeAnimate
{
    if (!_isStart || !_isPause)
    {
        return;
    }
    _isPause = NO;    
    //继续动画
    _startTime = [[NSDate date] timeIntervalSince1970];

    NSArray * keyPathArray = [_animation keyPathArrayWithPauseTime:_animateTime];
    CAKeyframeAnimation  * animate = [CAKeyframeAnimation animationWithKeyPath:_animation.keyPath];
    animate.values = keyPathArray;
    animate.duration = _animation.duration - _animateTime;
    [self.animateView.layer addAnimation:animate forKey:_animation.keyPath];
    [self resetViewWithPosition:_endPosition];
}
```

上面的方法也可以实现,但是每次都要重新创建动画,实在不是高明的方法.其实利用CAAnimation自身的属性,有更方便的方法.首先了解一下需要使用的这些属性

* beginTime指定了动画开始之前的的延迟时间.这里的延迟从动画添加到可见图层的那一刻开始测量,默认是0（就是说动画会立刻执行）.
* speed是一个时间的倍数,默认1.0,减少它会减慢图层/动画的时间,增加它会加快速度.如果2.0的速度,那么对于一个duration为1的动画,实际上在0.5秒的时候就已经完成了.当这个值为0时,动画静止.
* timeOffset和beginTime类似,但是和增加beginTime导致的延迟动画不同,增加timeOffset只是让动画快进到某一点,例如,对于一个持续1秒的动画来说,设置timeOffset为0.5意味着动画将从一半的地方开始.
* beginTime会受speed的影响,timeOffset并不受speed的影响.所以如果你把speed设为2.0,把timeOffset设置为0.5,那么你的动画将从动画最后结束的地方开始,因为1秒的动画实际上被缩短到了0.5秒.然而即使使用了timeOffset让动画从结束的地方开始,它仍然播放了一个完整的时长,这个动画仅仅是循环了一圈,然后从头开始播放.

### 全局时间

CoreAnimation有一个全局时间的概念,称作马赫时间(“马赫”实际上是 iOS和Mac OS系统内核的命名),它可以通过CACurrentMediaTime()获取.它与NSDate 或 CFAbsoluteTimeGetCurrent()不同, 后两者返回的时钟时间将会会网络时间同步，而CACurrentMediaTime() 是基于内建时钟的,不会因为外部时间变化而变化,这个方法获取到的时间，是手机从开机一直到当前所经过的秒数.

为了暂停在正确的位置以及继续动画时起点的位置正确,需要使用这个马赫时间来计算.

每个CALayer和CAAnimation实例都有自己本地时间的概念，是根据父图层/动画层级关系中的beginTime，timeOffset和speed属性计算。就和转换不同图层之间坐标关系一样，CALayer同样也提供了方法来转换不同图层之间的本地时间。

```
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
```

## 暂停/继续动画

利用CAAnimation的属性和全局时间,就可以实现暂停/继续动画了.代码如下:

```
//暂停动画
- (void)nx_pauseAnimate
{
    if (!_isStart || _isPause)
    {
        return;
    }

    _isPause = YES;

    CFTimeInterval pauseTime = [self.animateView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
    // 设置layer的timeOffset, 在继续操作也会使用到
    self.animateView.layer.timeOffset = pauseTime;
    // local time与parent time的比例为0, 意味着local time暂停了
    self.animateView.layer.speed = 0;
}
//继续动画
- (void)nx_resumeAnimate
{
    if (!_isStart || !_isPause)
    {
        return;
    }
    _isPause = NO;
    // 时间转换
    CFTimeInterval pauseTime = self.animateView.layer.timeOffset;
    // 计算暂停时间
    CFTimeInterval timeSincePause = CACurrentMediaTime() - pauseTime;
    // 取消
    self.animateView.layer.timeOffset = 0;
    // local time相对于parent time世界的beginTime
    self.animateView.layer.beginTime = timeSincePause;
    // 继续
    self.animateView.layer.speed = 1;
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://philm.gitbook.io/philm-ios-wiki/mei-zhou-yue-du/zai-caanimation-zhong-zan-ting-dong-hua.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
