Codepath

Container View Controllers Quickstart

Overview

Traditionally in iOS apps a view controller corresponds to one "screen" in the application. Apple provides a few container view controllers—such as the navigation controller and tab bar controller—to help developers manage and transition between multiple view controllers.

To build really unique user experiences, you can also define your own custom container view controllers that manage the display of and navigation between child view controllers. This guide provides a quick start recipe for doing this by building a simple container view controller TwoTabViewController that allows the user to switch between two view controllers.

Step 1: Create subclass of UIViewController

Create a new subclass of UIViewController by selecting File -> New -> File... -> Source -> Cocoa Touch Class. Tick the checkbox named Also create XIB file.

Step 2: Design container view controller in Interface Builder

You'll generally want a way for the user to control or navigate between the child view controllers. You'll probably also want add one or more blank view elements to serve as placeholders for the views of the child view controllers. Add @IBOutlets for any components you'll need programatically manipulate and @IBActions for any events to which you want to respond.

Step 3: Add code to keep track of child view controllers

You'll need a way to track of all the view controllers you are "containing". For example the navigation controller maintains a stack of view controllers. In our two tab example, we keep a separate property to reference each of the child view controllers.

class TwoTabViewController: UIViewController {
    ...
    var firstViewController: UIViewController?
    var secondViewController: UIViewController?
    ...
}

Step 4: Add logic to add and remove child views

The main responsibility of a container view controller is to display the child view controllers' views. You'll need to add code to keep track of which child views are currently being shown. You'll also need help iOS propagate events (such as viewDidLoad) to the child view controllers by calling certain methods before and after adding/removing a child view controller's view.

In our two tab example we keep a variable activeViewController. Whenever activeViewController is set we remove the old one's view and add the new one's view as a subview of our content view.

class TwoTabViewController: UIViewController {
    ...

    
    private var activeViewController: UIViewController? {
        didSet {
            removeInactiveViewController(inactiveViewController: oldValue)
            updateActiveViewController()
        }
    }

    private func removeInactiveViewController(inactiveViewController: UIViewController?) {
        if let inActiveVC = inactiveViewController {
            // call before removing child view controller's view from hierarchy
            inActiveVC.willMove(toParentViewController: nil)

            inActiveVC.view.removeFromSuperview()

            // call after removing child view controller's view from hierarchy
            inActiveVC.removeFromParentViewController()
        }
    }

    private func updateActiveViewController() {
        if let activeVC = activeViewController {
            // call before adding child view controller's view as subview
            addChildViewController(activeVC)

            activeVC.view.frame = contentView.bounds
            contentView.addSubview(activeVC.view)

            // call before adding child view controller's view as subview
            activeVC.didMove(toParentViewController: self)
        }
    }
    ...
}

Step 5: Handle events

You'll often have update which child view controllers' views are being displayed in response to system or user events. Here we set the active view controller to the be first one when our container view controller loads. We also update the active view controller in response to user taps.

class TwoTabViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        activeViewController = firstViewController
    }

    @IBAction func didTapFirstButton(sender: AnyObject) {
        activeViewController = firstViewController
    }

    @IBAction func didTapSecondButton(sender: AnyObject) {
        activeViewController = secondViewController
    }
}

Step 6: Use custom container in your application

As of this writing, there is no easy way to use and configure custom container view controllers in a storyboard. Your best bet is to instantiate and configure any custom container view controllers programmatically. If your custom container is the root view controller, a good place to do this is in the app delegate

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        let vc1 = UIViewController()
        let vc2 = UIViewController()

        vc1.view.backgroundColor = UIColor.orangeColor()
        vc2.view.backgroundColor = UIColor.purpleColor()

        let twoTabVC = TwoTabViewController(nibName: "TwoTabViewController", bundle: nil)
        twoTabVC.firstViewController = vc1
        twoTabVC.secondViewController = vc2

        window = UIWindow(frame: UIScreen.mainScreen().bounds)
        window?.rootViewController = twoTabVC
        window?.makeKeyAndVisible()

        return true
    }
    ...
}

Further reading

Fork me on GitHub