4
votes

I have a custom animation transition on my View Controller when I call the animation with an interactive delegate attached the animations work fine. When I add the interactive component that works properly. However, when I got to use a button to dismiss like I was before I had the interactive component the animation completion block is not getting called.

Below is the animation block. At first I was using the delay/options variation of the animation block and changed it to the simpler animation/completion to make sure it wasn't the function. If I remove all of the code out of the animation block it will execute and dismiss the controller (albeit, without the animation). I have no idea what cold be wrong and can't find a similar issue.

The gesture is simply swiping your finger to the right to dismiss the modal...this works fine. When I remove the interactive part the animation runs fine when pressing the button. This only happens when I attach the interactive component to the View Controller.

Here is the animation block:

[UIView animateWithDuration:PRESENT_DURATION animations:^{
            CGAffineTransform newTransform = fromViewController.view.transform;
            newTransform = CGAffineTransformTranslate(transform, adjustedBounds.size.width, height);
            fromViewController.view.transform = newTransform;
            toViewController.view.alpha = 1;
        } completion:^(BOOL finished){
            NSLog(@"Finished Animation");
            if ([transitionContext transitionWasCancelled]) {
                [transitionContext completeTransition:NO];
            } else {
                [fromViewController.view removeFromSuperview];
                [transitionContext completeTransition:YES];
            }
        }];

This is where I attach the animate and interactionController to the ViewController

- (IBAction)settingsButtonClicked:(id)sender
{
    UINavigationController *navigationController =[[self storyboard] instantiateViewControllerWithIdentifier:@"SettingsNavigationViewController"];
    navigationController.modalPresentationStyle = UIModalPresentationCustom;

    navigationController.transitioningDelegate = self;
    navigationController.edgesForExtendedLayout = UIRectEdgeNone;
    [self presentViewController:navigationController animated:YES completion:^{
        id animator = [navigationController.transitioningDelegate animationControllerForDismissedController:self];
        id interactor = [navigationController.transitioningDelegate interactionControllerForDismissal:animator];

        if ([interactor respondsToSelector:@selector(attachToViewController:)]) {
            [interactor attachToViewController:self];
        }
    }];
    navigationController.view.superview.frame = CGRectMake(0, 0, navigationController.view.frame.size.width, self.view.frame.size.height);
}
1

1 Answers

1
votes

I was having this exact same problem with using interactive percent driven transition when the pan driving the gesture would happen so quickly and it would cancel right after starting. I believe calling the finish/cancel too soon after presentation reveals a bug somewhere in apple code that forgets to call the completion block of the in-progress animation.

Finally got things working by overriding the cancelInteractiveTransition to update it with 0 percent as such:

override func cancelInteractiveTransition() {
    updateInteractiveTransition(0)
    super.cancelInteractiveTransition()
}

In obj C

-(void)cancelInteractiveTransition {
    [self updateInteractiveTransition:0];
    [super cancelInteractiveTransition];
}

Hope this gets you close to an answer, I would play around with doing something similar in the finishIneractiveTransition, maybe try something like dispatching the call to super on the next run loop

Edit: you could still get the animation to not complete by swiping back and forth with a lot of velocity so I had to add a small delay and settle on this for now:

override func cancelInteractiveTransition() {
    if percentComplete > 0.1 {
        updateInteractiveTransition(0)
        super.cancelInteractiveTransition()
    } else {
        var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue(), {
            self.updateInteractiveTransition(0)
            super.cancelInteractiveTransition()
        })
    }
}

override func finishInteractiveTransition() {
    if percentComplete > 0.1 {
        super.finishInteractiveTransition()
    } else {
        var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue(), {
            super.finishInteractiveTransition()
        })
    }
}