This article describes how, by adopting multiple distinct protocols simultaneously, you can design and reuse animated `UIView` transitions with and without `Storyboard`.
The Architect Approach: Zero coding.
While the first answer should be “use Storyboard Segues”, you can solve custom transitions this way:
Generic Approach
1. Modify your `CustomSegue` to adopt both `UIStoryboardSegue` and `UIViewControllerAnimatedTransitioning` protocols.
2. Refactor `CustomSegue` so that the animation can be used by both protocols.
3. Setup a `delegate` to the navigation controller, which can be itself, to supply the custom transitions to `push` & `pop`
4. Let `animationControllerForOperation` create and return an instance of `CustomSegue`, with an identifier of your choice.
Overview
// Adopt both protocols class CustomSegue: UIStoryboardSegue, UIViewControllerAnimatedTransitioning { func animate(firstVCView:UIView, secondVCView:UIView, containerView:UIView, transitionContext: UIViewControllerContextTransitioning?) { // factored transition code goes here }) { (Finished) -> Void in if let context = transitionContext { // UIViewControllerAnimatedTransitioning } else { // UIStoryboardSegue } } } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { // return timing } func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // Perform animate using transitionContext information // (UIViewControllerAnimatedTransitioning) } override func perform() { // Perform animate using segue (self) variables // (UIStoryboardSegue) } }
Complete code example.
Note: added a fade-in effect to emphasize the animation.
`CustomSegue`
// Animation for both a Segue and a Transition class CustomSegue: UIStoryboardSegue, UIViewControllerAnimatedTransitioning { func animate(firstVCView:UIView, secondVCView:UIView, containerView:UIView, transitionContext: UIViewControllerContextTransitioning?) { // Get the screen width and height. let offset = secondVCView.bounds.width // Specify the initial position of the destination view. secondVCView.frame = CGRectOffset(secondVCView.frame, offset, 0.0) firstVCView.superview!.addSubview(secondVCView) secondVCView.alpha = 0; // Animate the transition. UIView.animateWithDuration(self.transitionDuration(transitionContext!), animations: { () -> Void in firstVCView.frame = CGRectOffset(firstVCView.frame, -offset, 0.0) secondVCView.frame = CGRectOffset(secondVCView.frame, -offset, 0.0) secondVCView.alpha = 1; // emphasis }) { (Finished) -> Void in if let context = transitionContext { context.completeTransition(!context.transitionWasCancelled()) } else { self.sourceViewController.presentViewController( self.destinationViewController as! UIViewController, animated: false, completion:nil) } } } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 4 // four seconds } // Perform Transition (UIViewControllerAnimatedTransitioning) func animateTransition(transitionContext: UIViewControllerContextTransitioning) { self.animate(transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!.view, secondVCView: transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!.view, containerView: transitionContext.containerView(), transitionContext: transitionContext) } // Perform Segue (UIStoryboardSegue) override func perform() { self.animate(self.sourceViewController.view!!, secondVCView: self.destinationViewController.view!!, containerView: self.sourceViewController.view!!.superview!, transitionContext:nil) } }
`ViewController`
class ViewController: UIViewController, UINavigationControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.navigationController?.delegate = self } func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { switch operation { case .Push: return CustomSegue(identifier: "Abc", source: fromVC, destination: toVC) default: return nil } } }
Read more on StackOverflow question 31643228. (https://stackoverflow.com/a/31643461/218152)