Custom UIViewController Transitions

4,016 views

Published on

Creating custom animated and interactive transitions between UIViewControllers using iOS 7.0 API.

Published in: Software

Custom UIViewController Transitions

  1. 1. Custom UIViewController Transitions Ján Ilavský - @split82
  2. 2. iPhone OS 3.0
  3. 3. iOS 7
  4. 4. Modal View Controller - (void)presentViewController:animated:completion: ! - (void)dismissViewControllerAnimated:completion:
  5. 5. UIModalTransitionStyle modalTransitionStyle
  6. 6. typedef NS_ENUM(NSInteger, UIModalTransitionStyle) { UIModalTransitionStyleCoverVertical = 0, UIModalTransitionStyleFlipHorizontal, UIModalTransitionStyleCrossDissolve, #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 UIModalTransitionStylePartialCurl, #endif };
  7. 7. UIModalPresentationStyle modalPresentationStyle
  8. 8. typedef NS_ENUM(NSInteger, UIModalPresentationStyle) { UIModalPresentationFullScreen = 0, #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 UIModalPresentationPageSheet, UIModalPresentationFormSheet, UIModalPresentationCurrentContext, #endif #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 UIModalPresentationCustom, UIModalPresentationNone = -1, #endif };
  9. 9. typedef NS_ENUM(NSInteger, UIModalPresentationStyle) { UIModalPresentationFullScreen = 0, #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 UIModalPresentationPageSheet, UIModalPresentationFormSheet, UIModalPresentationCurrentContext, #endif #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 UIModalPresentationCustom, UIModalPresentationNone = -1, #endif };
  10. 10. Custom Fullscreen Non-interactive Transitions
  11. 11. Demo
  12. 12. TestViewController *viewController = [TestViewController new]; ! viewController.transitioningDelegate = ??? ! [self presentViewController:viewController animated:YES completion:nil];
  13. 13. TestViewController *viewController = [TestViewController new]; ! viewController.transitioningDelegate = ??? ! [self presentViewController:viewController animated:YES completion:nil];
  14. 14. @protocol UIViewControllerTransitioningDelegate <NSObject> ! @optional ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController: (UIViewController *)source; ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal: (id <UIViewControllerAnimatedTransitioning>)animator; ! ! @end
  15. 15. @protocol UIViewControllerTransitioningDelegate <NSObject> ! @optional ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController: (UIViewController *)source; ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal: (id <UIViewControllerAnimatedTransitioning>)animator; ! ! @end
  16. 16. @protocol UIViewControllerTransitioningDelegate <NSObject> ! @optional ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController: (UIViewController *)source; ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal: (id <UIViewControllerAnimatedTransitioning>)animator; ! ! @end
  17. 17. UIViewController UIViewController id <UIViewControllerTransitioningDelegate> transitioningDelegate animationControllerForPresentedController… animationControllerForDismissedController id <UIViewControllerAnimatedTransitioning> id <UIViewControllerAnimatedTransitioning> present dismiss
  18. 18. @protocol UIViewControllerAnimatedTransitioning <NSObject> ! ! - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext; ! - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; ! ! @optional ! - (void)animationEnded:(BOOL)transitionCompleted; ! @end
  19. 19. @protocol UIViewControllerAnimatedTransitioning <NSObject> ! ! - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext; ! - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; ! ! @optional ! - (void)animationEnded:(BOOL)transitionCompleted; ! @end
  20. 20. UIView UIView UIViewController UIViewController
  21. 21. @protocol UIViewControllerAnimatedTransitioning <NSObject> ! ! - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext; ! - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; ! ! @optional ! - (void)animationEnded:(BOOL)transitionCompleted; ! @end
  22. 22. @protocol UIViewControllerAnimatedTransitioning <NSObject> ! ! - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext; ! - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; ! ! @optional ! - (void)animationEnded:(BOOL)transitionCompleted; ! @end
  23. 23. UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ! UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; ! ! toViewController.view.alpha = 0.0f; toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; ! [transitionContext.containerView addSubview:toViewController.view]; [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  24. 24. ! Container View UIView UIView UIViewController UIViewController
  25. 25. UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ! UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; ! ! toViewController.view.alpha = 0.0f; toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; ! [transitionContext.containerView addSubview:toViewController.view]; [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  26. 26. UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ! UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; ! ! toViewController.view.alpha = 0.0f; toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; ! [transitionContext.containerView addSubview:toViewController.view]; [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  27. 27. initialFrameForViewController finalFrameForViewController from CGRectZero to CGRectZero
  28. 28. UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ! UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; ! ! toViewController.view.alpha = 0.0f; toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; ! [transitionContext.containerView addSubview:toViewController.view]; [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  29. 29. UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ! UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; ! ! toViewController.view.alpha = 0.0f; toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; ! [transitionContext.containerView addSubview:toViewController.view]; [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  30. 30. UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ! UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; ! ! toViewController.view.alpha = 0.0f; toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; ! [transitionContext.containerView addSubview:toViewController.view]; [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { //[fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  31. 31. Custom Non-Fullscreen Non-interactive Transitions
  32. 32. TestViewController *viewController = [TestViewController new]; ! viewController.modalPresentationStyle = UIModalPresentationCustom; viewController.transitioningDelegate = ??? ! [self presentViewController:viewController animated:YES completion:nil];
  33. 33. UIView UIView
  34. 34. Demo
  35. 35. Presentation != Dismissal
  36. 36. Presentation ! Container View UIView UIView UIViewController UIViewController
  37. 37. Dismission ! Container View UIView UIView UIViewController UIViewController
  38. 38. initialFrameForViewController finalFrameForViewController from to CGRectZero CGRectZero Presentation
  39. 39. initialFrameForViewController finalFrameForViewController from CGRectZero to CGRectZero Dismissal
  40. 40. viewWillDissapear ! viewDidDissapear FromViewController Presentation
  41. 41. viewWillAppear ! viewDidAppear ToViewController Dismissal
  42. 42. [transitionContext.containerView addSubview:toViewController.view]; ! toViewController.view.frame = CGRectInset([transitionContext initialFrameForViewController:fromViewController], 32.0f, 32.0f); ! toViewController.view.alpha = 0.0f; ! [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [transitionContext completeTransition:YES]; }]; Presentation
  43. 43. [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ fromViewController.view.alpha = 0.0f; } completion:^(BOOL finished) { [transitionContext completeTransition:YES]; }]; Dismissal
  44. 44. Interactive Transitions
  45. 45. start complete
  46. 46. start complete cancel finish updating
  47. 47. Demo
  48. 48. UIViewController UIViewController id <UIViewControllerTransitioningDelegate> transitioningDelegate animationControllerForPresentedController… animationControllerForDismissedController id <UIViewControllerAnimatedTransitioning> id <UIViewControllerAnimatedTransitioning> present dismiss
  49. 49. @protocol UIViewControllerTransitioningDelegate <NSObject> ! @optional ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController: (UIViewController *)source; ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal: (id <UIViewControllerAnimatedTransitioning>)animator; ! ! @end
  50. 50. @protocol UIViewControllerTransitioningDelegate <NSObject> ! @optional ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController: (UIViewController *)source; ! ! - (id <UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; ! ! - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal: (id <UIViewControllerAnimatedTransitioning>)animator; ! ! @end
  51. 51. UIViewController UIViewController id <UIViewControllerTransitioningDelegate> transitioningDelegate animationControllerForPresentedController… animationControllerForDismissedController id <UIViewControllerAnimatedTransitioning> id <UIViewControllerAnimatedTransitioning> present dismiss
  52. 52. UIViewController UIViewController id <UIViewControllerTransitioningDelegate> transitioningDelegate id <UIViewControllerAnimatedTransitioning> id <UIViewControllerAnimatedTransitioning> present dismiss id <UIViewControllerInteractiveTra nsitioning> id <UIViewControllerInteractiveTra nsitioning>
  53. 53. id <UIViewControllerInteractiveTransitioning>
  54. 54. @protocol UIViewControllerInteractiveTransitioning <NSObject> ! - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext; ! @optional ! - (CGFloat)completionSpeed; - (UIViewAnimationCurve)completionCurve; ! @end
  55. 55. UIPercentDrivenInteractiveTransition
  56. 56. UIPercentDrivenInteractiveTransition - (void)updateInteractiveTransition:(CGFloat)percentComplete; - (void)cancelInteractiveTransition; - (void)finishInteractiveTransition;
  57. 57. CGFloat scale = [gestureRecognizer scale]; switch ([gestureRecognizer state]) { case UIGestureRecognizerStateBegan: transitionController.interactive = YES; _startScale = scale; [testViewController dismissViewControllerAnimated:YES completion:nil]; } break; case UIGestureRecognizerStateChanged: { CGFloat percent = (1.0 - scale/_startScale); [transitionController.percentDrivenInteractiveTransition updateInteractiveTransition: (percent <= 0.0) ? 0.0 : percent]; break; } case UIGestureRecognizerStateEnded: case UIGestureRecognizerStateCancelled: if ([gestureRecognizer velocity] >= 0.0 || [gestureRecognizer state] == UIGestureRecognizerStateCancelled) { [transitionController.percentDrivenInteractiveTransition cancelInteractiveTransition]; } else { [transitionController.percentDrivenInteractiveTransition finishInteractiveTransition]; } break; default: break; }
  58. 58. CGFloat scale = [gestureRecognizer scale]; switch ([gestureRecognizer state]) { case UIGestureRecognizerStateBegan: transitionController.interactive = YES; _startScale = scale; [testViewController dismissViewControllerAnimated:YES completion:nil]; } break; case UIGestureRecognizerStateChanged: { CGFloat percent = (1.0 - scale/_startScale); [transitionController.percentDrivenInteractiveTransition updateInteractiveTransition: (percent <= 0.0) ? 0.0 : percent]; break; } case UIGestureRecognizerStateEnded: case UIGestureRecognizerStateCancelled: if ([gestureRecognizer velocity] >= 0.0 || [gestureRecognizer state] == UIGestureRecognizerStateCancelled) { [transitionController.percentDrivenInteractiveTransition cancelInteractiveTransition]; } else { [transitionController.percentDrivenInteractiveTransition finishInteractiveTransition]; } break; default: break; }
  59. 59. Core Animation! ! +[UIView transitionFromView:toView:duration:options:completion:]! ! Custom Animations! ! UIView block-based animations UIPercentDrivenInteractiveTransition
  60. 60. [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition:YES]; }];
  61. 61. [UIView animateWithDuration:self.duration delay:0.0 options:0 animations:^{ toViewController.view.alpha = 1.0f; } completion:^(BOOL finished) { [fromViewController.view removeFromSuperview]; [transitionContext completeTransition: !transitionContext.transitionWasCancelled]; }];
  62. 62. - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext { ! _transitionContext = transitionContext; _toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; _toViewController.view.alpha = 0.0f; _toViewController.view.frame = [transitionContext finalFrameForViewController:_toViewController]; [transitionContext.containerView addSubview:_toViewController.view]; } id <UIViewControllerInteractiveTransitioning>
  63. 63. id <UIViewControllerInteractiveTransitioning> - (void)updateInteractiveTransition:(CGFloat)percentComplete { _toViewController.view.alpha = percentComplete; [_transitionContext updateInteractiveTransition:percentComplete]; }
  64. 64. id <UIViewControllerInteractiveTransitioning> - (void)cancelInteractiveTransition { [_transitionContext cancelInteractiveTransition]; [UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^{ _toViewController.view.alpha = 0.0f; } completion:^(BOOL finished) { [_transitionContext completeTransition:! _transitionContext.transitionWasCancelled]; }]; }
  65. 65. viewWillAppear viewDidAppear viewWillDisappear viewDidDisappear
  66. 66. viewWillAppear viewDidAppear viewWillDisappear viewDidDisappear
  67. 67. viewWillAppear viewWillDisappear viewDidDisappear
  68. 68. - (void)viewWillAppear:(BOOL)animated { [self doSomeSideEffectsAssumingViewDidAppearIsGoingToBeCalled]; id <UIViewControllerTransitionCoordinator> coordinator; coordinator = [self transitionCoordinator]; if(coordinator && [coordinator initiallyInteractive]) { [transitionCoordinator notifyWhenInteractionEndsUsingBlock: ^(id <UIViewControllerTransitionCoordinatorContext> ctx) { if(ctx.isCancelled) { [self undoSideEffects]; } }]; } } UIViewControllerTransitionCoordinator
  69. 69. UINavigationController – pushViewController:animated: – popViewControllerAnimated: – popToRootViewControllerAnimated: – popToViewController:animated:
  70. 70. - (id<UIViewControllerAnimatedTransitioning>)! navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC ! toViewController:(UIViewController *)toVC UINavigationControllerDelegate - (id<UIViewControllerInteractiveTransitioning>)! navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController: (id<UIViewControllerAnimatedTransitioning>)animationController
  71. 71. UINavigationController @property(nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer
  72. 72. UITabBarController @property(nonatomic, assign) UIViewController *selectedViewController @property(nonatomic) NSUInteger selectedIndex
  73. 73. UITabBarControllerDelegate - (id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController; ! - (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC;
  74. 74. finty fň
  75. 75. // Snapshot UIView *fromView = [fromViewController.view snapshotViewAfterScreenUpdates:NO]; ! // Interactivity fromViewController.view.userInteractionEnabled = NO; toViewController.view.userInteractionEnabled = YES; transitionContext.containerView.userInteractionEnabled = YES; ! [transitionContext.containerView addSubview:fromView]; [transitionContext.containerView addSubview:toViewController.view]; ! // Finish before animation [transitionContext completeTransition:YES]; ! [UIView animateWithDuration: . . .
  76. 76. // Prepare BitmapContext CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); GLubyte *textureData = malloc(textureWidth * textureHeight * 4); memset_pattern4(textureData, "0000", textureWidth * textureHeight * 4); NSUInteger bytesPerPixel = 4; NSUInteger bytesPerRow = bytesPerPixel * textureWidth; NSUInteger bitsPerComponent = 8; CGContextRef bitmapContext = CGBitmapContextCreate(textureData, textureWidth, textureHeight, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); CGColorSpaceRelease(colorSpace); // draw [view.layer renderInContext:bitmapContext]; CGContextRelease(bitmapContext); ! // set data for texture glBindTexture(GL_TEXTURE_2D, texture); // set bitmap data into texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData); // Don't need this data anymore free(textureData);
  77. 77. fuck off view controllers
  78. 78. Custom UIViewController Transitions Ján Ilavský - @split82

×