55

I have a basic spinning animation of the iPhone. Is there any way that I can "pause" the animation so that the position of the view will be maintained? I guess one way of doing this would be to cause the animation to "complete" instead of calling "remove" on it, how would I do that?

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"];
mclaughj
  • 12,645
  • 4
  • 31
  • 37

6 Answers6

167

Recently appeared Apple's technical note QA1673 describes how to pause/resume layer's animation.

Pause and resume animations listing is below:

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

Edit: iOS 10 introduced new API - UIViewPropertyAnimator that allows to handle animations more interactively, for example it makes easy to pause and resume animation or 'seek' it to some particular progress value.

Vladimir
  • 170,431
  • 36
  • 387
  • 313
  • This is working for me just fine, HOWEVER, when I in a paused state, and rotate my device, I lose all ability to interact with the application. It hasn't actually crashed, however it appears "frozen". Is there a possible conflict with "willAnimateRotationToInterfaceOrientation" ? – cohen72 Jan 06 '13 at 09:29
  • @YoCoh, it indeed can pause also standard rotation animations for a view, and as during animations user interaction may be disabled (probably that's the case) and standard animation does not finish you end up with UI stuck in disabled state. not sure how to workaround it – Vladimir Jan 06 '13 at 18:58
  • @Vladimir How to resume animation in reversed direction? I understand that speed must be negative in this case, but what about `timeOffset` and `beginTime`? – efimovdk Mar 31 '16 at 15:28
  • This only works reliably if `layer.speed = 1.0` in the "resume" code. Setting it to any other value (e.g. to match the original animation speed) causes the "resume" to jump unnaturally. – David James Dec 22 '16 at 15:04
18

Answer For Swift 3:

Credits @Vladimir

Code:

 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
}
invisible squirrel
  • 2,968
  • 3
  • 25
  • 26
Supratik Majumdar
  • 2,365
  • 1
  • 23
  • 31
7

Set the current state of your view's layer to match the state of the presentationLayer, then remove the animation:

CALayer *pLayer = [myView.layer presentationLayer];
myView.layer.transform = pLayer.transform;
[myView.layer removeAnimationForKey:@"rotationAnimation"];
gerry3
  • 21,420
  • 9
  • 66
  • 74
  • 4
    Although this saves the position, it doesn't serve as the starting point when I re-add the animation to the view, the view moves slightly slower to accommodate for the shorter distance in the same amount of time. Is there another step that needs to be taken to be able to resume the animation where it left off? – mclaughj Mar 18 '10 at 04:14
4

Credit to @Vladimir and @Supratik-Majumdar

Swift 5.1 as a CALayer extension

    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
        }
    }
B25Dec
  • 2,301
  • 5
  • 31
  • 54
Rand
  • 165
  • 1
  • 1
  • 12
0

You can use a timer or handle the animation delegate method:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

Here is my code:

// ...
[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];
}

Hope it can help you.

h7ing
  • 1
  • 4
    CAUTION!!! thats's not the recommended way of doing it unless you have some special need to do it this way. Imagine you have an infinite running animation. This method will not help when we are pausing the animation before app enters background and then try to resume it when it enters foreground. – aloha Jan 30 '12 at 21:23
-1

Simplest One

self.viewBall.layer.position = self.viewBall.layer.presentationLayer().position
Chetan Prajapati
  • 2,249
  • 19
  • 24