Custom Transition

Besides what looks like it’s more simple and “hack” than you think, the truth is, we always see at the beginning, the iOS transitions as a very difficult thing, and it’s not ‘weird’ to see that this is one of the reason that we always look for a library to delivery this to us, but do we really need this?

We have pieces in order to make the transition, but the most important is one delegate , we need to implement the UIViewControllerTransitioningDelegate protocol, in a very simple way this will be the protocol/class that will be responsible for our transition.

In a very simple way this is the normal navigation flow:

Normal transition without custom transition

We have our first view controller and we want to go to the second one with a custom transition. We want to get back from the second to the first using a custom transition.

We need to “name” an object/class between this 2 view controllers to perform our custom animation transition, think like we call a layer between this two that will be responsible for this transition, this will become this:

Having a delegate that handles our custom transition

This is not something new, actually every time that you perform any transition, just calling you next view you are using this, it’s just the default one, what we are doing is using a custom one that “override” the default.

Create one demo project, any format that you want, in our scenario we are using the “Main” storyboard when we create a new project, rename the default view controller that Xcode creates from “ViewController” to “FirstViewController”, and create another view controller and name as “SecondViewController” just to be more visual.

Go to your storyboard and create the respective view controller for the SecondViewController too.

Inside each view controller, for the first one add one button with the string “Go to next” and in the second view controller view create one button with the string “Go Back”.

You should have something like this:

Views for you test you view controller transition

Add the respective @IBAction in each view controller:

First View Controller:

@IBAction func goNext(_ sender: Any) {

}

And for second view controller:

@IBAction func getBack(_ sender: Any) {

}

Let’s move to the part about the transition itself, create our custom transition delegate, for this create a class called TransitionDelegate and implement two methods:

First, the method that will be responsible for presenting view controller:

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {



}

Now let’s implement the method that will be responsible to when we dismiss, get back to the previous view controller:

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {



}

Ok, we do know that we need to have the “delegate” or “coordinator” of our transition, but this is just who is responsible for transition, not the animation itself, for this, when implementing the custom class to be responsible for this custom transition, we need to set how will be our transition, the effect itself that we want to make, as this is the purpose, have a custom transition, you may see a warning from the Xcode we will fix this latter.

After implemented the delegate we just set-up two methods, and they are expecting an object of type UIViewControllerAnimatedTransitioning , now it's the other important part, first we are setting up the class that will be responsible for the transition, but the reason that we are doing this is about the effect itself, and we need to create / have one object of this type, he will be the one that will make our transition effect.

Let’s create one simple effect for you understand, create one class that inheritance from NSObject and add this content:

import UIKit class FadePushAnimator: NSObject, UIViewControllerAnimatedTransitioning {



func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {

return 0.5

}



func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

guard

let toViewController = transitionContext.viewController(forKey: .to)

else {

return

}

transitionContext.containerView.addSubview(toViewController.view)

toViewController.view.alpha = 0



let duration = self.transitionDuration(using: transitionContext)

UIView.animate(withDuration: duration, animations: {

toViewController.view.alpha = 1

}, completion: { _ in

transitionContext.completeTransition(!transitionContext.transitionWasCancelled)

})

}

}

Let’s dig into to understand the “why’s”.

1 — Transition Duration: this is where our delegate will ask how long should be our transition, it’s mandatory.

2 — Animate Transition: Here is where the “magic” happens, it’s the effect itself, let’s look into the implementation and understand.

2.1 — This animation we want to only be made when I’m going “to”, this is why we validate first this, we get the “transition context”, imagine like a “box” that when we started our navigation has the reference for which screen we are calling and where we are coming from.

2.2 — We add the “view” from the view controller to the transition context container view, what we are doing is adding to the “context” the view that we want to be animated, as the animation is basically we have the “snapshot” that we set to a “temporary place” and apply the effects that we want, without interfering in the original view.

2.3 — The animation part, this is just the normal format what we make animation, you actually can use any other format of animation here, CALayer for example, anything to do your animation.

We have the animation for when we go, it’s good to have one to get back, as we already seeing the difference’s, so let’s create one to when we get back.

import UIKit class FadePopAnimator: NSObject, UIViewControllerAnimatedTransitioning {



func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {

return 0.5

}



func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

guard

let fromViewController = transitionContext.viewController(forKey: .from),

let toViewController = transitionContext.viewController(forKey: .to)

else {

return

}



transitionContext.containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view)



let duration = self.transitionDuration(using: transitionContext)

UIView.animate(withDuration: duration, animations: {

fromViewController.view.alpha = 0

}, completion: { _ in

transitionContext.completeTransition(!transitionContext.transitionWasCancelled)

})

}

}

We will not dig too deep, as now you can understand some differences, like, now we check if we have the view that I’m going back using transitionContext.viewController(forKey: .from)

In this context what we are doing is “fade out” and for this reason, we add both views to our context container, but, we add the view that we will get back below the actual view, this is because it’s “connected” to the way that I want to make my animation.

One thing that I have not mentioned before when the animation finish it’s mandatory to call the method completeTransition( we do this for example inside the completion block of the UIView animation function.

For me I like to organize my class’s in a separated place, it’s an option, but this is how my structure looks like:

Project structure

Now we have all the pieces in order to apply our animation let’s start to fulfil all the missing spot’s in order to have the animation, the most simple one is getting back to our class TransitionDelegate and set the animation that we will use for each transition step, first let set the present / push animation.

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

return FadePushAnimator()

}

We just have to create the instance FadePushAnimator() and return in this method.

Let’s do the same when we dismiss / pop.

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

return FadePopAnimator()

}

It’s the same, now we just return the instance FadePopAnimator() for when we dismiss.

We just need to configure our view controller transition delegate to use our custom transition delegate class.

In the first view controller first create the instance of our delegate, like this:

let transitionDelegate: UIViewControllerTransitioningDelegate = TransitionDelegate()

In the view did load set this view transition delegate to our custom delegate like this:

override func viewDidLoad() {

super.viewDidLoad() self.transitioningDelegate = transitionDelegate

}

In this step basically you have everything ready, now we just need to call our second view controller, as we are not using navigation controller we need to call the method presentViewController:animated:completion: , inside the method @IBAction func goNext(_ sender: Any) let's do like this:

let secondView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: String(describing: SecondViewController.self))

secondView.transitioningDelegate = transitionDelegate

self.present(secondView, animated: true, completion: nil)

This should not be new for you, we get the instance from the storyboard, after having the instance we set the second view controller transition delegate to use the same as we are using, I don’t need to recreate, besides I can do and nothing will change.

And finally just call the present method, setting that we want to be animated the system is smart enough to verify the delegate, as we have will them use our custom delegate.

In the second view controller, I would say that is even less code, go to the second view controller and inside the @IBAction func getBack(_ sender: Any) just add this:

self.dismiss(animated: true, completion: nil)

Now it’s just run, you will see that the face transition that we just configured will work fine, and there’s no much more than this.

Let’s do a simple recap of the step’s that we need in a very simple format:

Class responsible for the transition, for us, is TransitionDelegate , this will be the delegate that we will set for all the view controllers that we want to have a custom transition. The object that has our custom transition, we have one to push and one for pop.

Final Considerations

Just this, it’s not that complicated, right? In a very simple way we set this transition, for navigation controller’s the principle it’s exactly the same, the delegate implements another method of course, but use the same “components”, I will create another tutorial for this situation too.

I hope you liked, and if you have any comment please share with me, want to ask for something, let me know, If from here you did some code, let me know too, share your code, there’s no bad code or “junior” code, share what you did, you may help others sharing your code!!!

Please share as most as possible as this helps me reach more people and continue writing, found any mistake? Get in touch let’s help together!

Link to the final project you can find here: https://github.com/felipeflorencio/CustomViewControllerTransitionSample

Apple Reference Documentation: https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html

Conclusion

I hope you liked this, and if you have any comments please share them with me, if you want to ask for something, let me know, and please, hit the “clap” button as this helps deliver this content to more people.

Please share as most as possible as this helps me reach more people and continue writing, found any mistake? Get in touch let me know!

Also, I started a new channel on YouTube, I want to teach in videos what I write about, so please, subscribe if you liked this tutorial.

Youtube channel: https://www.youtube.com/channel/UC6FlmTDymN19ZWDN5jsnamA

Twitter: @ProgrammerPath

Linkedin: https://www.linkedin.com/in/felipeflorenciodev/?locale=en_US