Weekly Swift articles, podcasts and tips by John Sundell.

Child View Controllers

Published on 08 Jan 2019

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
parent.view.addSubview(child.view)

// Then, add the child to the parent
parent.addChild(child)

// 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
child.removeFromParent()

// Finally, remove the child’s view from the parent’s
child.view.removeFromSuperview()

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) {
        addChild(child)
        view.addSubview(child.view)
        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 {
            return
        }

        willMove(toParent: nil)
        view.removeFromSuperview()
        removeFromParent()
    }
}

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() {
        super.viewDidLoad()

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

        // Center our spinner both horizontally & vertically
        NSLayoutConstraint.activate([
            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()
        add(loadingVC)

        loader.load { [weak self] content in
            loadingVC.remove()
            self?.render(content)
        }
    }
}

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:

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! 🚀