Codepath

Navigation Controller Quickstart

Updated with iOS 17, Xcode 15.0.1 and Swift 5.9

Overview

Navigation controllers help a user browse through a hierarchy of content by maintaining a history of view controllers that navigated to the current one. This quickstart covers the basic procedure for setting up and using a navigation controller.

Storyboard setup

Step 1: Embed root view controller inside a navigation controller

In your storyboard, select the initial view controller in your hierarchy. With this view controller selected, choose the menu item Editor -> Embed In -> Navigation Controller.

Step 2: Add components to control the navigation

Typically navigating to another view controller happens in response to some user interaction. You'll have to add some controls (e.g. buttons, gesture recognizers, table views) to your root view controller to allow the user to trigger the navigation.

In our demo example below we have added two buttons "Red" and "Blue" that will trigger a navigation to new view controllers with red and blue backgrounds.

Step 3: Create a segue to navigate to a new view controller

Add the view controller you want to navigate to in the storyboard. You can trigger navigation in response to an action by control-dragging from the component that would fire the action to the new view controller and selecting show under Action Segues. This defines a transition between the two view controllers called a segue.

In the example below we create two segues, one in response to a tap on the "Red" button and the other in response to a tap on the "Blue" button. This can be done for a wide variety of events. One typical example is handling the row selection event in a table view by control-dragging from a prototype cell.

Step 4: Set an identifier for the segue

We'll need a way figure out which segue is firing when we write the code to configure the new view controller. You can set a unique identifier for each segue in the Attributes Inspector after selecting it.

Step 5: Prepare view controller before navigating to it

To configure the new view controller to which we are navigating, we can override the prepareForSegue method in the current view controller. In this case, we check which segue was triggered and change the background color of the destination view controller appropriately.

class ViewController: UIViewController {
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let destinationVC = segue.destination as UIViewController
        if segue.identifier == "redSegue" {
            destinationVC.title = "Red"
            destinationVC.view.backgroundColor = UIColor.systemRed
        } else if segue.identifier == "blueSegue" {
            destinationVC.title = "Blue"
            destinationVC.view.backgroundColor = UIColor.systemBlue
        }
    }
}

Running this example gives us:


Programmatic setup

You can also set up a navigation controller programmatically. If you need the navigation controller to be your root view controller the best place to do this is in the app delegate. We'll reimplement the same example as above.

Step 1: Instantiate the root view controller for the navigation controller

In this case we load the color picker view controller from our example above. In a non-storyboard application we would instantiate a view controller here directly or by loading a nib with initWithNibName.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let pickColorVC = storyboard.instantiateViewController(withIdentifier: "PickAColor") as UIViewController
        ...
    }
    ...
}

Step 2: Create navigation controller with root view controller

Here we create a navigation controller and provide it with the color picker view controller as its root view controller. We also set the root view controller of the window object to be the navigation controller.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        ...
        let navigationController = UINavigationController(rootViewController: pickColorVC)

        window = UIWindow(frame: UIScreen.main.bounds)
        if let window = window {
          window.rootViewController = navigationController
          window.makeKeyAndVisible()
        }
        return true
    }
    ...
}

Step 3: Respond to events by pushing new view controllers

Here we respond to button taps by configuring a new view controller and pushing it onto the navigation stack with pushViewController.

class ColorPickerViewController: UIViewController {

    @IBAction func didTapRedButton(sender: Any) {
        pushViewController(title: "Red", color: UIColor.red)
    }

    @IBAction func didTapBlueButton(sender: Any) {
        pushViewController(title: "Blue", color: UIColor.blue)
    }

    private func pushViewController(title: String, color: UIColor) {
        let vc = UIViewController()
        vc.view.backgroundColor = color
        vc.title = title
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

Further reading

Fork me on GitHub