UIViewController – issue with custom dismiss transition

A few potential gotchas with dismissal of modally presented view controllers using custom transition animations:

  • Add the presented ("to") view to the container, then bring the presented view front. Don't add the presenting view as you might remove it from its current superview.
  • On dismiss, UIKit sets the alpha of the presented view to 0 before animateTransition is called. So you'll want to set it to 1.0 or whatever it was at the completion of the present before firing off your dismiss animation(s).
  • Likewise for the presented view's transform. On dismiss it gets reset to identity before your animateTransition is called.

Given all that, I think this should work:

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containerView = transitionContext.containerView;
    
    const BOOL isUnwinding = [toController presentedViewController] == fromController;
    const BOOL isPresenting = !isUnwinding;
    
    UIViewController *presentingController = isPresenting ? fromController : toController;
    UIViewController *presentedController = isPresenting ? toController : fromController;
    
    [containerView addSubview:presentingController.view];
    [containerView bringSubviewToFront:presentingController.view];
    
    if(isPresenting)
    {
        // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
        presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
        presentedController.view.alpha = 0.7;
        
        [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
            // Lift up the presented controller.
            presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
            
            // Brighten the presented controller (out of shadow).
            presentedController.view.alpha = 1;
            
            // Push the presenting controller down the screen – 3d effect to be added later.
            presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
        } completion: ^(BOOL finished){
            [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
        }];
    }
    else
    {
        presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
        presentedController.view.alpha = 0.7;
        
        [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
            // Bring the presenting controller back to its original position.
            presentingController.view.layer.transform = CATransform3DIdentity;
            
            // Lower the presented controller again and put it back in to shade.
            presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
            presentedController.view.alpha = 0.4;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
        }];
    }
}