Search code examples
iosobjective-ccocoacore-animationobjective-c-blocks

iOS Simultaneous animations without completion block


I have a video player that has a standard toolbar. The toolbar is dismissed by a swipe down gesture. I also have a view (a panel really) that can appear directly above the toolbar and is also dismissed by a swipe down gesture. When both the panel and the toolbar are open, one swipe down gesture should dismiss the panel, a second will dismiss the toolbar. Problem is that when the swipe gestures occur quickly back-to-back (before the panel animation completes) then the toolbar animation jitters.

- (void)handleSwipe:(UISwipeGestureRecognizer *)gestureRecognizer
{
    UISwipeGestureRecognizerDirection direction = [gestureRecognizer direction];

    if (direction == UISwipeGestureRecognizerDirectionDown) {
        if (![toolbar isHidden]) {
            // Only dismiss the bottom panel if it is open
            if (_selectedSegmentIndex != UISegmentedControlNoSegment) {
                _selectedSegmentIndex = UISegmentedControlNoSegment;
                [bottomPanelView dismissPanel];
            } else {
                CGRect tempRect = CGRectMake(0, self.view.frame.size.height, toolbar.frame.size.width, toolbar.frame.size.height);
                [UIView animateWithDuration:0.25f
                                 animations:^{
                                    // Move the toolbar off the screen.
                                    toolbar.frame = tempRect;
                                 }
                                 completion:^(BOOL finished) {
                                     [toolbar setHidden:YES];
                                 }];
            }
        }
    }
}

[bottomPanelView dismissPanel] is in a separate class and is not aware of the class that calls it. It has the follow animation...

[UIView animateWithDuration:self.panelAnimationDuration
                      delay:0.0
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     // slideOutLocation is off the screen 
                     self.view.frame = slideOutLocation;
                 }
                 completion:^(BOOL finished) {
                     [self.view removeFromSuperview];
                     [self removeFromParentViewController];
                     self.panelActive = NO;
                 }];

So basically, the dismissPanel animation is still running when the animation to dismiss the toolbar begins. When performing a double swipe in slow motion in the simulator, the first animation looks fine, but the toolbar animation is jittery.

I know how to nest animations in the completion block, but that cannot be done here since dismissing both the panel and the toolbar is not always what is wanted. Also, the dismissPanel code is handled elsewhere and is not in control of the toolbar.

Is there a way to allow multiple animation blocks to run simultaneously without putting the completion block? Let me know if any clarification is needed! Thanks!


Solution

  • I wonder if the problem might have to do with auto layout (setting frames while auto layout is on causes problems). I tried a simple test of animating a view and a tool bar off the bottom of the screen by animating their constraint constants, and the animation looked fine. I made IBOutlets to their respective bottom constraints (called viewBottomCon and toolBarBottomCon).

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.isFirstSwipe = YES;
    }
    
    -(IBAction)downSwipe:(UISwipeGestureRecognizer *)sender {
        if (self.isFirstSwipe) {
    
            self.viewBottomCon.constant = -52;
            self.isFirstSwipe = NO;
            [UIView animateWithDuration:5 animations:^{
                [self.view layoutIfNeeded];
            } completion:nil];
    
        }else if (!self.isFirstSwipe) {
    
            self.toolBarBottomCon.constant = -44;
            [UIView animateWithDuration:3 animations:^{
                [self.view layoutIfNeeded];
            } completion:nil];
        }
    }
    

    This is a simpler setup than yours, but I think it should work in your case too.