简体   繁体   中英

Pausing CALayer animation with an animation with a delay

I have a set of nested UIView animations (2 or 3 levels deep at a given time) that I would like to be able to pause and resume. Some of these animations use -animateWithDuration:animations:completion: while others use -animateWithDuration:delay:options:animations:completion: in order to delay execution of the animation block.

I read and implemented Technical Q&A QA1673 about pausing all animations in a layer tree, but I'm encountering an issue with the animations that use a delay parameter. I can pause and resume animations just fine, but when the animation resumes, any animation block that has a delay associated with it appears to have its delay extended by the amount of time that the layer tree was paused. So for example, if one of the blocks has a delay of 1 second, and the layer tree was paused for 3 seconds, the animation delays for 4 seconds after resuming. I'm guessing this has something to do with the beginTime property? Any help would be appreciated.

// Pause and Resume methods, right from the technical Q&A
- (void)pauseAnimationsOnLayer:(CALayer *)layer
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
}

- (void)resumeAnimationsOnLayer:(CALayer *)layer
{
    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer.beginTime = timeSincePause;
}

// Chained animations
- (void)animateNextPopup
{
    [UIView animateWithDuration:kRFPVictorySequenceStatePopupDuration
                     animations:^{
                         [_currentStateImageView setHidden:NO];
                         [_currentStateImageView setTransform:CGAffineTransformIdentity];

                     }
                     completion:^(BOOL finished) {
                         [UIView animateWithDuration:kRFPVictorySequenceStateSlideOffDuration
                                               delay:kRFPVictorySequenceStateVoteDelay
                                             options:UIViewAnimationOptionCurveEaseInOut
                                          animations:^{
                                              if (winnerIsDem) {
                                                  [_currentStateImageView setFrame:CGRectMake(-_currentStateImageView.frame.size.width, 
                                                                                              _currentStateImageView.frame.origin.y, 
                                                                                              _currentStateImageView.frame.size.width, 
                                                                                              _currentStateImageView.frame.size.height)];
                                              }
                                              else {
                                                  [_currentStateImageView setFrame:CGRectMake(1024, 
                                                                                              _currentStateImageView.frame.origin.y, 
                                                                                              _currentStateImageView.frame.size.width, 
                                                                                              _currentStateImageView.frame.size.height)];
                                              }
                                          }
                                          completion:^(BOOL finished) {
                                              // Do some stuff
                                          }
                          ];
                     }
     ];
}

I found the solution to the problem! You have to reset the self.layer.beginTime value to zero in the completion block of your animations.

eg

[UIView animateWithDuration:element.duration 
                      delay:element.delay 
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                      // Animate properties here!
                     }
                 } completion:^(BOOL finished){
                              // Reset BeginTime all the time
                              // So, in case a pause took place the delay values are valid again!
                         **self.layer.beginTime = 0.0f;**
                 }];

The rest of the pause / resume code stays exactly the same.

Best!

I suggest a different approach.

Animation blocks are easy to implement, but useful only if you do not need any control over your animation.

Otherwise, you should use a timer and create your own animation manually.

[NSTimer scheduledTimerWithTimeInterval:0.1
                                 target:self
                               selector:@selector(timerFired)
                               userInfo:nil
                                repeats:YES];

- (void)timerFired
{
    if (isPaused) {
        // Do nothing
    } else {
        // Animate
    }
}

- (IBAction)pauseTapped:(id)sender
{
    if (isPaused) {
        isPaused = NO;
    } else {
        isPaused = YES;
    }
}

isPaused is a flag that control your animation state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM