A split view controller manages the presentation of two child view controllers in a two-column layout also called a master-detail interface. The view controller displayed in the left column is called the primary (master) view controller and the view controller on the right side is called the secondary (detail) view controller. Actions by the user in the master view controller change what is displayed in the detail view controller.

The great thing about UISplitViewController is that it automatically adapts the presentation of its child view controllers based on the available width. For example on an iPad where the horizontal size class is regular, it has a master-detail interface but when running on an iPad in split view or on an iPhone where the horizontal size class is compact it behaves exactly like a navigation controller.

In this blog post, I will show you how to use a split view controller in your app.

Setting up a split view controller

First, let’s have a look at how to set up our split view controller. I usually create my UI in code but the process is not that much different when done in Interface Builder. A typical split view controller setup usually involves two navigation controllers, one that contains the master view controller and the other that contains the detail view controller. Let’s look at an example of building a contacts application:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.backgroundColor = .white

        // Create an instance of split view controller and set display mode to .allVisible to make sure it displays master detail in portrait on iPad.
        let splitViewController = UISplitViewController()
        splitViewController.preferredDisplayMode = .allVisible

        // Create an instance of the master view controller and embed it in a navigation controller.
        let contactListViewController = ContactListViewController()
        let primaryViewController = UINavigationController(rootViewController: contactListViewController)

        // Create an instance of the detail view controller and embed it in a navigation controller.
        let contactDetailViewController = ContactDetailViewController()
        let secondaryViewController = UINavigationController(rootViewController: contactDetailViewController)

        // Assign the navigation controllers to the split view.
        splitViewController.viewControllers = [primaryViewController, secondaryViewController]

        // Make the master view controller the delegate of the split view controller
        splitViewController.delegate = masterViewController

        // Make the detail view controller the delegate of the master view controller
        masterViewController.delegate = self

        window?.rootViewController = splitViewController
        window?.makeKeyAndVisible()

        return true
    }

Showing the right data in the detail view controller

Now that our split view controller is setup we need to make sure that when the user selects a contact in the master list the data is displayed in the detail view controller. For this, we create a delegate protocol through which the master view controller can communicate with the detail view controller.

protocol ContactListViewControllerDelegate {

  func contactListViewController(_ controller: ContactListViewController, didSelectContact: Contact)
}

In the implementation of the table view delegate method tableView(_:didSelectRowAt:) we get a reference to the contact and pass it into the didSelectContact method of the delegate. Then inside of our detail view controller, we make sure to update the UI every time the delegate method is called.

Because in a master-detail interface the detail view controller is always visible you will immediately see the details of the selected contact however, on the iPhone nothing happens. This is because we need to call the showDetailViewController(_:sender:) method. This method walks up through the parent view controller hierarchy to find the container view controller which should handle the presentation of the detail view controller. In compact horizontal environment (iPhone) when the split view controller is collapsed the split view controller will ask the primary view controller to handle the presentation. In our case this is a navigation controller which will respond by pushing the detail view controller on the stack.

Showing a different detail view controller

With more complicated designs you sometimes want to show a different type of detail view controller depending on which cell is selected. For example, in our contact list we could decide to show a profile section on the top for which we will show a different style of view controller. This also can be done by presenting your view controller through the showDetailViewController(_: sender:) method. Again when the split view is collapsed it will simply push the new detail view controller on the stack of the primary navigation controller, but when it is expanded it will replace the detail view controller in the right column of the split view.

Conclusion

As we have seen split view controller is great for building user interfaces that need to look great on iPhone and iPad and with iPad apps for Mac soon also on the Mac. We took a look at how to setup a split view controller and how to show detail view controllers. There is much more you can do to customize a split view controller for example through its delegate. I encourage you to go and try it out in your apps.

Contact me on Twitter @kairadiagne if you have any questions, comments or feedback.