In this episode we take the Magic Move transition from last week's episode and make interactive, so that you can feel the transition along with your swipe.
Episode Links Source Code UIViewInteractiveTransitioning Protocol Reference UIPercentDrivenInteractiveTransition Class Reference Creating an Interactive Pan protocol PanInteractionControllerDelegate { func interactiveAnimationDidStart(controller: PanInteractionController) } class PanInteractionController : UIPercentDrivenInteractiveTransition { var pan: UIPanGestureRecognizer? var delegate: PanInteractionControllerDelegate? var isActive: Bool { get { return pan?.state != .Possible } } func attachToView(view: UIView) { pan = UIPanGestureRecognizer(target: self, action: Selector("screenEdgePan:")) view.addGestureRecognizer(pan!) } func screenEdgePan(pan: UIScreenEdgePanGestureRecognizer) { let view = pan.view! switch pan.state { case .Began: let location = pan.locationInView(view) if location.x < CGRectGetMidX(view.bounds) { delegate?.interactiveAnimationDidStart(self) } case .Changed: let translation = pan.translationInView(view) let percent = fabs(translation.x / CGRectGetWidth(view.bounds)) updateInteractiveTransition(percent) case .Ended: if pan.velocityInView(view).x > 0 { finishInteractiveTransition() } else { cancelInteractiveTransition() } case .Cancelled: fallthrough case .Failed: cancelInteractiveTransition() case .Possible: break } } } Attaching the interaction to DetailViewController Since the interactive portion of our animation only happens from the DetailViewController, we can have it self contained in this class. If this were a deep hierarchy of view controllers inside of a navigation controller, we'd want to move this to the navigation controller delegate to reuse some code. First we need to conform to our delegate so we know when the animation starts: class DetailViewController : UIViewController, UINavigationControllerDelegate, PanInteractionControllerDelegate { //... } Then we need to change our interaction controller to reference the new class: var interactionController = PanInteractionController() When the view loads, we attach the interaction controller, and set the delegate to self to be notified when the interaction starts. override func viewDidLoad() { super.viewDidLoad() interactionController.delegate = self interactionController.attachToView(view) } } We also need to tell the navigation controller that we need to pop (animated) when the interaction starts, so that it will actually ask our controller for the proper animation controller and interaction controller for the animation: func interactiveAnimationDidStart(controller: PanInteractionController) { navigationController?.popViewControllerAnimated(true) } Lastly, we vend the appropriate objects when the navigation controller asks for them: func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { if operation == .Pop { let animator = Animator() animator.presenting = false return animator } else { return nil } } func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { if interactionController.isActive { return interactionController } else { return nil } }