Episode #86

iOS 7 View Controller Transitions

18 minutes
Published on September 19, 2013

This video is only available to subscribers. Get access to this video and 586 others.

Now that the iOS 7 is out and the NDA has been lifted you can expect NSScreencast to cover lots of the new features. In this episode I show how you can take a simple stock modal transition for a color picker and create something more unique and playful that fits with the "swatch" theme.

Episode Links

There's not much documentation around this yet, but I suspect a guide will pop up soon enough. For now we have to stick with the original WWDC 2013 Session video 218 (I had to watch this a few times) and the class docs.

Creating a custom transition

typedef NS_ENUM(NSInteger, SwatchTransitionMode){
    SwatchTransitionModePresent = 0,
    SwatchTransitionModeDismiss
};

@interface SwatchTransition : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) SwatchTransitionMode mode;
@end
#import "SwatchTransition.h"
const CGFloat SWATCH_PRESENT_DURATION = 1.5;
const CGFloat SWATCH_DISMISS_DURATION = 0.25;

@implementation SwatchTransition

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
    return self.mode == SwatchTransitionModePresent ? SWATCH_PRESENT_DURATION : SWATCH_DISMISS_DURATION;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    CGRect sourceRect = [transitionContext initialFrameForViewController:fromVC];

    CGAffineTransform rotation = CGAffineTransformMakeRotation(- M_PI / 2);
    UIView *container = [transitionContext containerView];

    if (self.mode == SwatchTransitionModePresent) {
        [container addSubview:toVC.view];

        toVC.view.layer.anchorPoint = CGPointZero;
        toVC.view.frame = sourceRect;
        toVC.view.transform = rotation;

        [UIView animateWithDuration:SWATCH_PRESENT_DURATION
                              delay:0
             usingSpringWithDamping:0.25
              initialSpringVelocity:3
                            options:UIViewAnimationOptionCurveEaseIn
                         animations:^{
                             toVC.view.transform = CGAffineTransformIdentity;
                         } completion:^(BOOL finished) {
                             [transitionContext completeTransition:YES];
                         }];
    } else if(self.mode == SwatchTransitionModeDismiss) {
        [UIView animateWithDuration:SWATCH_DISMISS_DURATION
                              delay:0
                            options:UIViewAnimationOptionCurveEaseIn
                         animations:^{
                             fromVC.view.transform = rotation;
                         } completion:^(BOOL finished) {
                             [fromVC.view removeFromSuperview];
                             [transitionContext completeTransition:YES];
                         }];
    }
}

@end

In the code above, we capture the two view controllers that we're transitioning between from the UIViewControllerContextTransitioning protocol. This also gives us our container view and some initial CGRects we can work with.

We then add the target view to the hierarchy and rotate it, anchored to the upper-left corner. Animating it in is as simple as setting the transform back to the identity transform.