Child View Controllers

Just like how a UIView can be added to another UIView to form a hierarchy, a view controller can become the child of another view controller. That enables us to compose our UI from multiple building blocks, which can lead to smaller view controller implementations that are easier to reuse.

When added as a child, a view controller is automatically resized according to the size of the app’s window — but just like a subview of a stand-alone UIView, a child view controller’s view can be resized and repositioned using either frames or Auto Layout constraints.

To add a view controller as a child, we use the following three API calls:

let parent = UIViewController()
let child = UIViewController()

// First, add the view of the child to the view of the parent

// Then, add the child to the parent

// Finally, notify the child that it was moved to a parent
child.didMove(toParent: parent)

And to remove a child once added to a parent, we use the following three calls:

// First, notify the child that it’s about to be removed
child.willMove(toParent: nil)

// Then, remove the child from its parent

// Finally, remove the child’s view from the parent’s

As you can see above, both of these operations require quite a few steps — so if we start using child view controllers extensively in a project, things can get repetitive quite quickly.

One solution to that problem is to add an extension on UIViewController that bundles up all the steps required to either add or remove a child view controller into two easy-to-use methods, like this:

extension UIViewController {
    func add(_ child: UIViewController) {
        child.didMove(toParent: self)

    func remove() {
        // Just to be safe, we check that this view controller
        // is actually added to a parent before removing it.
        guard parent != nil else {

        willMove(toParent: nil)

Child view controllers are especially useful for UI functionality that we wish to reuse across a project. For example, we might want to display a loading view as we’re loading the content for each screen — and that can easily be implemented using a child view controller, that can then simply be added when needed.

To do that — first, let’s create a LoadingViewController that displays a loading spinner at the center of its view, like this:

class LoadingViewController: UIViewController {
    override func viewDidLoad() {

        let spinner = UIActivityIndicatorView(style: .gray)
        spinner.translatesAutoresizingMaskIntoConstraints = false

        // Center our spinner both horizontally & vertically
            spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor)

Next, when we — in one of our content view controllers — start loading content, we can now simply add our new LoadingViewController as a child to display a loading spinner, and then remove it once we’re done:

class ContentViewController: UIViewController {
    private let loader = ContentLoader()

    func loadContent() {
        let loadingVC = LoadingViewController()

        loader.load { [weak self] content in

Pretty cool! 👍 But the question is — why go through the trouble of implementing a view controller for something like a loading spinner, instead of just using a plain UIView? Here are some common reasons:

  • A view controller gets access to events like viewDidLoad and viewWillAppear, even when used as a child, which can be really useful for many kinds of UI code.
  • A view controller is more self-contained — and can both include the logic required to drive its UI, as well as the UI itself.
  • When added as a child, a view controller automatically fills the screen, reducing the need for additional layout code for full screen UIs.
  • When a piece of UI is implemented as a view controller, it can be used in many different contexts — including both being pushed onto a navigation controller, as well as being embedded as a child.

Of course that doesn’t necessarily mean that all UIs should be implemented using child view controllers - but they’re a good tool to keep in mind in order to build UIs in a more modular fashion.

Thanks for reading! 🚀

▶️ Grab the playground containing all of the sample code from this article.

Beyond the basics

Ready to explore more advanced ways of using child view controllers in Swift? Check out these articles:

Unit Testing

Grand Central Dispatch