1.27. Presenting Master-Detail Views with UISplitViewController

Problem

You want to take maximum advantage of the iPad’s relatively large screen by presenting two side-by-side view controllers.

Solution

Use the UISplitViewController class.

Discussion

Split view controllers are present only on the iPad. If you’ve used an iPad, you’ve probably already seen them. Just open the Settings app in landscape mode and have a look. Can you see the split view controller there in Figure 1-70?

Split view controller in the Settings app on the iPad

Figure 1-70. Split view controller in the Settings app on the iPad

A split view controller has left and right sides. The left side displays the main settings, and tapping on each one of those settings shows the details of that setting item on the right side of the split view controller.

Warning

Never attempt to instantiate an object of type UISplitViewController on a device other than an iPad. This will raise an exception.

Apple has made it extremely easy to create split view controller based applications. Simply follow these steps to create your app based on split view controllers:

  1. In Xcode, navigate to the File menu and choose New New Project...

  2. In the New Project screen, pick iOS Application on the left side and then pick Master-Detail Application (as shown in Figure 1-71) and press Next.

  1. In this screen, pick your product name and make sure your device family is Universal. We want to make sure our app runs both on the iPhone and the iPad. Once you are done, press Next (see Figure 1-72).

    Picking the Master-Detail Application project template in Xcode

    Figure 1-71. Picking the Master-Detail Application project template in Xcode

    Setting the master-detail project settings in Xcode

    Figure 1-72. Setting the master-detail project settings in Xcode

  2. Now pick where you would like to save your project. Once done, press the Create button.

Now the project is created. In the Scheme breadcrumb button on the top-left corner of Xcode, make sure your app is set to run on iPad Simulator instead of iPhone Simulator. If you create a universal master-detail app in Xcode, Xcode makes sure that your app runs on the iPhone as well, but when you run your app on the iPhone, the structure of the app will be different. It will have a navigation controller with a view controller inside it, whereas running the same app on the iPad will use a split view controller with two view controllers inside it.

There are two files that are very important to note in the split view controller project template:

MasterViewController

The master view controller that appears on the left side of the split view controller on the iPad. On the iPhone, it is the first view controller that the user sees.

DetailViewController

The detail view controller that appears on the right side of the split view controller on the iPad. On the iPhone, it is the view controller that gets pushed onto the stack once the user taps on any of the items on the root (first, master) view controller.

Now you need to think about communication between the master and the detail view controller. Do you want the communication to be done through the app delegate, or do you want the master view controller to send messages to the detail view controller directly? That’s really up to you.

If you run the app in iPad Simulator, you’ll notice that in landscape mode, you can see our master and detail view controllers in the split view controller, but as soon as you rotate the orientation to portrait, your master view controller is gone and is replaced with a master navigation button on the top-left side of the navigation bar of the detail view controller. Although this is good, we weren’t expecting it, since we were comparing it with the Settings app on the iPad. If you rotate the settings app to portrait on an iPad, you can still see both the master and the detail view controllers. How can we accomplish this? It turns out Apple has exposed an API to us through which we can do it. Simply go to the DetailViewController.m file and implement this method:

- (BOOL) splitViewController:(UISplitViewController *)svc
    shouldHideViewController:(UIViewController *)vc
               inOrientation:(UIInterfaceOrientation)orientation{
    return NO;
}

If you return NO from this method, iOS will not hide the master view controller in either orientation, and both the master and the detail view controllers will be visible in both landscape and portrait orientations. Now that we have implemented this method, we won’t need those two methods anymore:

- (void)splitViewController:(UISplitViewController *)splitController
     willHideViewController:(UIViewController *)viewController
          withBarButtonItem:(UIBarButtonItem *)barButtonItem
       forPopoverController:(UIPopoverController *)popoverController{
    barButtonItem.title = NSLocalizedString(@"Master", @"Master");
    [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
    self.masterPopoverController = popoverController;
}

- (void)splitViewController:(UISplitViewController *)splitController
     willShowViewController:(UIViewController *)viewController
  invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem{
    [self.navigationItem setLeftBarButtonItem:nil animated:YES];
    self.masterPopoverController = nil;
}

These methods were there simply to manage the navigation bar button for us, but now that we are not using that button anymore, we can get rid of the methods. You can comment them out or just remove them from the DetailViewController.m file.

If you look inside your master view controller’s header file, you’ll notice something similar to this:

#import <UIKit/UIKit.h>

@class DetailViewController;

@interface MasterViewController : UITableViewController

@property (strong, nonatomic) DetailViewController *detailViewController;

@end

As you can see, the master view controller has a reference to the detail view controller. Using this connection, we can communicate selections and other values to the detail view controller, as you will soon see.

By default, if you run your app now in iPad Simulator, you will see a UI similar to that shown in Figure 1-73. The default implementation that Apple provides us with in the master view controller has a mutable array that gets populated with instances of NSDate every time you press the plus (+) button on the navigation bar of the master view controller. The default implementation is very simple, and you can modify it by learning a bit more about table views. Please refer to Chapter 4 for more details about table views and populating them.

An empty split view controller running on iPad Simulator

Figure 1-73. An empty split view controller running on iPad Simulator

Get iOS 7 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.