有没有一种方法可以暂停CABasicAnimation?

55

我有一个基本的iPhone旋转动画。有没有办法“暂停”动画以便保持视图的位置?我猜做到这一点的一种方法是让动画“完成”而不是在其上调用“remove”,我该如何做到这一点?

CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2];
rotationAnimation.duration = 100;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = HUGE_VALF;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
[myView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
6个回答

167

最近出现了苹果公司的技术说明QA1673,介绍了如何暂停/恢复图层的动画。

以下是暂停和恢复动画的示例:

-(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;
}

编辑: iOS 10引入了新的API——UIViewPropertyAnimator,允许更交互地处理动画,例如它使暂停和恢复动画或将其“寻找”到某个特定进度值变得容易。


这对我来说工作得很好,但是,当我处于暂停状态并旋转我的设备时,我失去了与应用程序交互的所有能力。它实际上没有崩溃,但它看起来“冻结”了。是否可能与“willAnimateRotationToInterfaceOrientation”存在冲突? - cohen72
@YoCoh,它确实可以暂停视图的标准旋转动画。由于在动画期间用户交互可能被禁用(可能是这种情况),并且标准动画没有完成,因此您最终会得到处于禁用状态的UI。不确定如何解决它。 - Vladimir
@Vladimir 如何在相反的方向上恢复动画?我知道速度必须是负数,但timeOffsetbeginTime呢? - efimovdk
只有在“恢复”代码中layer.speed = 1.0时才能可靠地工作。将其设置为任何其他值(例如,与原始动画速度匹配)会导致“恢复”不自然地跳转。 - David James

18

Swift 3 的答案:

感谢 @Vladimir

代码:

 func pauseAnimation(){
  let pausedTime = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
  layer.speed = 0.0
  layer.timeOffset = pausedTime
}

func resumeAnimation(){
  let pausedTime = layer.timeOffset
  layer.speed = 1.0
  layer.timeOffset = 0.0
  layer.beginTime = 0.0
  let timeSincePause = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
  layer.beginTime = timeSincePause
}

7

将视图层的当前状态设置为与presentationLayer的状态相匹配,然后删除动画:

CALayer *pLayer = [myView.layer presentationLayer];
myView.layer.transform = pLayer.transform;
[myView.layer removeAnimationForKey:@"rotationAnimation"];

4
虽然这样做可以保存位置,但在我重新将动画添加到视图时,视图会略微地慢一些以适应相同时间内较短的距离,这并不是一个起点。是否还需要采取其他步骤才能使动画从停止的地方继续播放? - mclaughj

4

鸣谢 @Vladimir 和 @Supratik-Majumdar

将 Swift 5.1 作为 CALayer 扩展

    extension CALayer
    {
        func pauseAnimation() {
            if isPaused() == false {
                let pausedTime = convertTime(CACurrentMediaTime(), from: nil)
                speed = 0.0
                timeOffset = pausedTime
            }
        }

        func resumeAnimation() {
            if isPaused() {
                let pausedTime = timeOffset
                speed = 1.0
                timeOffset = 0.0
                beginTime = 0.0
                let timeSincePause = convertTime(CACurrentMediaTime(), from: nil) - pausedTime
                beginTime = timeSincePause
            }
        }

        func isPaused() -> Bool {
            return speed == 0
        }
    }

我该如何更改动画速度值?只有当值为1.0时才有效。有什么想法吗? - balkaran singh
我从未尝试过将动画速度更改为0或1以外的其他值。 - Rand

0
您可以使用计时器或处理动画委托方法:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

这是我的代码:

// ...
[self startAnimation];
// ...

- (void)startAnimation {
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.fromValue = [NSNumber numberWithFloat:0];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_2_PI];
rotationAnimation.duration = 1.0;
rotationAnimation.cumulative = YES;
// rotationAnimation.repeatCount = 0; // <- if repeatCount set to infinite, we'll not receive the animationDidStop notification when the animation is repeating
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.delegate = self; // <- hanlde the animationDidStop method
[myView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];

}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (shouldContinueAnimation) // <- set a flag to start/stop the animation
    [self startAnimation];
}

希望这可以对您有所帮助。


4
注意!!!除非您有某些特殊需要,否则这不是推荐的做法。想象一下您有一个无限运行的动画。当我们在应用程序进入后台之前暂停动画,然后在其进入前台时尝试恢复它时,这种方法将不起作用。 - aloha

-1

最简单的一个

self.viewBall.layer.position = self.viewBall.layer.presentationLayer().position

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接