In earlier posts, I created some examples of iOS view controller transitions. Those examples were set up in a way that would allow for the simplest explanation of the concepts, but were not architected using the ‘best practices’ I would use when setting up a transition in my own projects. In this post, I’ll share some ideas that can improve reusability and stability of the code.
Delegate Logic in Separate Controllers
Ideally, controllers B and C could each maintain their own arbitrary transitioning behavior. In earlier examples, we accomplished this by having the controllers assign themselves as the delegate of the navigation controller. This approach is error-prone because the controllers are fighting over who gets to be the delegate. If we’re traveling between scenes B and C several times, which controller is the delegate and when? It’s easy to get mixed up here and create bugs. If, like most apps, our app has more than three scenes, it becomes that much more confusing. My solution is to create a custom UINavigationController subclass for scene A. This controller will be its own delegate:
We can set it up to implement the UINavigationControllerDelegate functions that UINavigationController uses to query for transition-related objects:
Every transition has a source controller and a destination controller. This UINavigationController subclass forwards the delegate calls to the source controller. If the source controller doesn’t implement the UINavigationControllerDelegate protocol, we use the destination controller instead. By doing it this way, we’re guaranteed to be calling one of the controllers that is actually involved in this transition. We also need the mostRecentController property to keep a reference to the controller we just called. This next function will need to forward another call to the same controller:
This example is a subclass of UINavigationController, but the same ideas apply if you’re dealing with a UITabBarController.
Grouping Gesture Logic with Percent Driven Animation
In the previous interactive transition example, the UIGestureRecognizer code was included in scene B’s controller. What if we wanted another scene with a similar pinching transition? Instead of duplicating the gesture handling code, we should abstract it out into a different class. We can most easily do this by creating a UIPercentDrivenInteractiveTransition subclass that handles the gesture. The header for this interactive transition class would look something like this:
Now that all the gesture handling code has been abstracted away, our scene B controller looks like this:
This controller has a small amount of code compared to the previous implementation, which makes it easier for any other controllers to reuse the TTInteractivePinchTransition object to get similar results.
Beefing Up the UIViewControllerAnimatedTransitioning Object
If we’re going to have several types of transitions, we might as well share some of the boilerplate code that they all use. We can do this by writing a base class that all the UIViewControllerAnimatedTransitioning classes will extend. In this example, all subclasses will inherit the duration behavior for free:
I hope this discussion of best practices has been useful, and maybe even inspired you to look for similar ideas elsewhere in your code. There are a few extra useful snippets of code that I skipped over in this post. They can all be found in the example code (link below).
The code for this post is available in the ‘Step5_bestPractices’ directory here.