Custom TabBarController Transition.
Today, i will be writing a simple custom tabBarController transition.
When we have implemented the tabBarController the normal way, and have its childViewControllers set, whenever we press the tabBar item on the tabBar, the view is jumped to the pressed tab’s view.
But there are times when we would like the view transitioning to be different. In this post, i will be writing a simple guide on how to make a Fade transitioning.
UITabBarControllerDelegate
For a tabBarController, transitioning animation lies in its UITabBarControllerDelegate, the UITabBarControllerDelegate implements a function called :
tabBarController(_:animationControllerForTransitionFrom:to:)
which requires a returning of UIViewControllerAnimatedTransitioning object.
UIViewControllerAnimatedTransitioning will be the place where the magic of the view transitioning happens, but before that, we need to create a custom UITabBarControllerDelegate.
Side Note: We can also conform a class to UITabBarControllerDelegate. A popular solution is often conforming the AppDelegate to UITabBarControllerDelegate and implements the tabBarController(_:animationControllerForTransitionFrom:to:) function in an extension. This is because setting up of tabBarController usually takes place in appDelegate and setting the delegate = self is convenient.
But in today’s example, i will create a custom UITabBarControllerDelegate class.
I name the class as FadeTBTransitioningDelegate. Remember to conform the class to NSObject because UITabBarControllerDelegate requires it.
And in the tabBarController(_:animationControllerForTransitionFrom:to:), i return an object called FadeTBAnimatedTransitioning. This is a class which conforms to UIViewControllerAnimatedTransitioning Protocol and where all the magic of transitioning happens.
UIViewControllerAnimatedTransitioning
UIViewControllerAnimatedTransitioning contains the four following functions:
- animateTransition(using:) — required
- transitionDuration(using:) — required
- interruptibleAnimator(using:)
- animationEnded(_:)
In our simple example, we will not be implementing the interruptibleAnimator. But this function comes in handy when you want to implement an transitioning animation which can be interrupted.
animationEnded(_:) is the function that we use to do clean up after the animation is done. It will always be called after the animation is done. In our simple example, we will not be implementing it as well.
transitionDuration(using:) is the function that lets us indicate the duration of the transition animation. It is a required function. Here we implement a duration of 0.4 second animation transitioning.
animateTransition(using:) is the function that we implement the view transitioning. It is, too, a require function. To implement a Fade Animation, the codes are as such:
Explanation on animateTransition(using:)
For animateTransition(using:), it contains a transitionContext object, most of the incoming/outgoing views & viewController should come from this transitionContext.
in 1:
We get the incomingView from transitionContext.
in 2:
We get the containerView from transitionContext. For every viewController transitioning, even for navigationController’s push transitioning or a presentation transitioning, there will always be a containerView. This containerView acts, as its name suggests, a container view that helps to transition the outgoing view and remove it automatically when the animation ends, and transition the incoming view into the desired frame.
in 3:
we set the incomingView’s alpha to 0 and add the incomingView to the containerView.Two things to note here.
- when we get the containerView from transitionContext, the containerView comes with the outgoingView already added to it and it will remove the outgoingView when animation is done automatically. But we have to manually add the incomingView to the containerView.
- The frame of the incomingView derived from transitionContext is the supposed final frame of the incomingView. Thus, we don’t have to change the frame of the incomingView in this case. But if we are implementing, let say, a push transition, then there is a need to change the frame of this incomingView so that it is out of the screen and animate the movement of the incomingView to this final frame.
in 4:
We perform the animation by setting the alpha back to 1 to achieve a fade in animation.
in 5:
Always remember to call the transitionContext.completeTransition(success) function when the animation is done. Or else, the animationEnded(_:) will not be called and everything will be stuck here.
