Swap root view controllers using a custom container view controller
How a custom container view controller can help you move seamlessly between different top level navigation hierarchies in your app.
When a user first opens a freshly installed app, it is not uncommon that the user has to go through some sort of onboarding and registration / sign in process before the user can start to enjoy the main content of the app. And each of these steps typically has its own navigation hierarchy. As a quick and dirty way of moving between each of these steps and navigation hierarchies, I’ve seen a lot of people just swap out the nearest UIWindow’s rootViewController. This works, but has its limitations.
A much more powerful way to swap between different navigation hierarchies, is to create your own container view controller. It makes it easy to animate the transition between the different hierarchies, as well as making sure the lifecycle of your view controllers and their views is respected. A container view controller is also a natural fit when using Coordinators, or similair patterns. You would use your custom container view controller as you would use a UINavigationController, UITabBarController, or any other of the container view controllers already included in UIKit.
The term “container view controller” might sound intimidating, but is noting more than an ordinary UIViewController with one or more child view controllers.
Also, there is nothing wrong with having, say, a UINavigationController or a UITabViewController as a child view controller of a custom container view controller. They are view controllers like any other view controller. Note, there are some special cases among UIKit’s own container view controllers. For instance, UIKit does not allow a UITabBarController to be a child of a UINavigationController.
Key takeaways
- Create a subclass of UIViewController that will act as the container view controller. Expose methods that let users of this container view controller modify the view controllers managed by it (like UINavigationController’s
setViewControllers(_:animated:)
). - To add a child view controller, call the following methods in order:
addChild(childViewController)
,view.addSubview(childViewController.view)
andchildViewController.didMover(toParent: self)
. Set constraints, and configure any animations, right after the child’s view has been added to the container view controller’s view hierarchy. If you add animations, make sure thatchildViewController.didMove(toParent: self)
is called after the animation has completed (e.g. in the animation’s completion block). - To remove a child view controller, call the following methods in order:
childViewController.willMove(toParent: nil)
,childViewController.view.removeFromSuperview()
andchildViewController.removeFromParent()
. Remove constraints, and configure any animations, right afterchildViewController.willMove(toParent: nil)
has been called. If you add animations, make sure thatchildViewController.removeFromParent()
is called after the animation has completed (e.g. in the animation’s completion block). - You would then typically subclass your custom container view controller depending on the use case. E.g. you might create a subclass called RootController, and let it be the root view controller of your app. Or another subclass called “NewsFeedController”, and let it manage differend kinds of news feed view controllers — like “TrendingNewsViewController” and “FreshNewsViewController”.
Apple also have a nice article about this topic: Creating a Custom Container View Controller. The documentation for UIViewController has additional information as well.
If you just want to get your hands on some example code right away, the complete code example used in this article can be found here: https://github.com/thomsmed/ios-examples/tree/main/ContainerViewController
The example app
We’ll create a simple example app where we can use our custom container view controller. The app will have no functionality by its own, but will contain a typical tree of navigation hierarchies. And we’ll see how our custom container view controller can fit into that.
We’ll use a variant of the Coordinator pattern, but will instead call them Flow Controllers and let them all be container view controllers.
To make the process of creating Auto Layout Constraints more fun, we’ll use the awesome library Cartography.
The navigation hierarchy
The app will contain three top level navigation hierarchies:
- Onboarding, with a UIPageViewController (aka OnboardingFlowController) acting as the root container view controller.
- Registration / Login, with a UINavigationController (aka LoginFlowController) acting as the root container view controller.
- Main Content, with a UITabBarController (aka MainFlowController) acting as the root container view controller.
At the absolute root of our app, we’ll create a AppFlowController. It will be initiated by our UIApplicationDelegate implementation, and will also be responsible for creating SceneFlowControllers to be assigned as the root view controller to the UIWindow associated with our UIWindowSceneDelegate implementation.
The SceneFlowController will act as the one responsible for managing the navigation between the three top level navigation hierarchies (Onboarding, Registration / Login and Main Content. The SceneFlowController will also be a subclass of our custom container view controller.
SinglePageController
In lack of a better name, we’ll call our custom container view controller SinglePageController. It will only contain one child / managed view controller at a time, and we’ll create methods to make it easy to switch out this view controller when we want to — with and without animation.
Exposed for subclasses, and who ever might use our SinglePageController, we’ll create the method setViewControllers(_: using:)
. This will make it easy to swap out the currently showing child view controller — with or without animation.
SceneFlowController
As mentioned before, we’ll have our SceneFlowController be a subclass of our custom container view controller (SinglePageController). It will utilise the methods of its superclass when swapping between the three top level navigation hierarchies.
The result looks and behave really nice! And with a custom container view controller you are free to be as creative as you want when it comes to animating the transition from one top level navigation hierarchy to the next one.
Summary
In this article we have made our very own container view controller, and used it to move between the three different top level navigation hierarchies of our app. With a custom container view controller it is easy to animate transitions between navigation hierarchies, and at the same time making sure the lifecycle events of our view controllers and their views are respected.
As I mentioned earlier in this article, the complete source code can be found here: https://github.com/thomsmed/ios-examples/tree/main/ContainerViewController
Happy coding!