Question

I have the following animation block, to do some kind of a marquee label effect.
I have two labels and I scroll both, when the scroll animation is finished, I set new frames and scroll again. the animation never stops.

- (void)scrollAwayWithInterval:(NSTimeInterval)interval delay:(NSTimeInterval)delay {
    [UIView animateWithDuration:interval
                          delay:delay
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         self.label.frame = _labelDestRect;
                         self.subLabel.frame = _subLabelDestRect;
                     }
                     completion:^(BOOL finished) {
                         if (finished) {
                             if (self.subLabel.frame.origin.x > 0) {
                                 [self prepareFramesForStep2];
                             } else {
                                 [self prepareFramesForStep1];
                             }

                             [self scrollAwayWithInterval:interval delay:delay];
                         }
                     }];
}

I need a way to pause/resume the animation, so I want to do the same animation with CABasicAnimation.

So my question is, what is the equivalent way to do the following animation with CABasicAnimation ?

Was it helpful?

Solution

It is actually possible to pause UIView animations. It works because under the covers, iOS creates CAAnimations on the layer of the view(s) that you are animating. I have a sample project on github called KeyframeViewAnimations that demonstrates exactly this.

If you are animating 2 different views at once, you might need to either manipulate the speed property on the layer of the superview that contains both views, or pause the animation on both view's layers separately. (I haven't tried pausing UIView animations on multiple views at the same time before, so I'm not totally sure if setting the speed property on the superview's layer will work or not.)

Edit: I just tried it, and setting the speed on the superview's layer pauses both animations. The pause/resume code I put on a pause/resume button looks like this:

- (IBAction)handlePauseButton:(UIButton *)sender
{
  animationIsPaused = !animationIsPaused;
  if (animationIsPaused)
  {
    [pauseButton setTitle: @"Resume" forState: UIControlStateNormal];
    CALayer *theLayer = animationContainerView.layer;

    //Freeze the animation.
    theLayer.speed = 0;

    //Calculate how far we into the animation based on current media time minus the media time when
    //The animation started.

    //Shift the layer's timing so it appears at the current time.
    theLayer.timeOffset = CACurrentMediaTime()-theLayer.beginTime;
    theLayer.beginTime = 0;
  }
  else
  {
    [pauseButton setTitle: @"Pause" forState: UIControlStateNormal];
    CALayer *theLayer = animationContainerView.layer;

    CFTimeInterval pausedTime = [theLayer timeOffset];

    //Now reset the time offset and beginTime to zero.
    theLayer.timeOffset = 0.0;
    theLayer.beginTime = 0.0;

    //Figure out how much time has elapsed since the animation was paused.
    CFTimeInterval timeSincePause = CACurrentMediaTime() - pausedTime;

    //Set the layer's beginTime to that time-since-pause
    theLayer.beginTime = timeSincePause;

    //Start the animation running again
    theLayer.speed = 1.0;
  }
}

OTHER TIPS

You could still use this block.

All you need to add is a BOOL variable that you can set to NO in a press of a button (or however you want) and the just set it as:

- (void)scrollAwayWithInterval:(NSTimeInterval)interval delay:(NSTimeInterval)delay {
    [UIView animateWithDuration:interval
                          delay:delay
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         self.label.frame = _labelDestRect;
                         self.subLabel.frame = _subLabelDestRect;
                     }
                     completion:^(BOOL finished) {
                         if (finished) {
                             if (shouldContinueAnimation) {
                                 if (self.subLabel.frame.origin.x > 0) {
                                     [self prepareFramesForStep2];
                                 } else {
                                     [self prepareFramesForStep1];
                                 }

                                 [self scrollAwayWithInterval:interval delay:delay];
                             }
                         }
                     }];
}

where shouldContinueAnimation is your BOOL.

Hope it helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top