Advanced iOS Summer Bundle

3 brand-new books on SwiftUI, Combine and Catalyst — $99.99 for a limited time!

This content has been archived.

iPad for iPhone Developers 101 in iOS 6: UISplitView Tutorial

This is the first part of a three-part series to help get iPhone Developers up-to-speed with iPad development by first focusing on three of the most useful classes: UISplitView, UIPopoverController, and Custom Input Views.


  • Other, Other, Other
Let's Display a List of Monsters in a Split View!

Let's Display a List of Monsters in a Split View!

Update 3/7/2013: Fully updated for iOS 6 (original post by Ray Wenderlich, update by Ellen Shapiro).

This is the first part of a three-part series to help get iPhone Developers up-to-speed with iPad development by first focusing on three of the most useful classes: UISplitView, UIPopoverController, and Custom Input Views. (Jump to Part 2 or Part 3 in the series.)

In this series, you’ll make an iPad app from scratch that makes use of all of the three most useful basic capabilities. First, the app will display a list of monsters from one of Ray’s Cocos2D games in a split view. Then, you’ll change the color of a label using a popover view. Finally, you’ll be able to change a monster’s weapon by using a custom input view.

By the end, you’ll have a good handle of some of the most important features of iPad development and will be able to hit the ground running!

Adding a Split View

In iPad development, it would rarely make sense to have a full-screen UITableView like you do so often in iPhone programming – there’s just too much space. To make better use of that space, the UISplitViewContoller comes to the rescue.

The Split View lets you carve up the iPad screen into two sections and display a view controller in each side. It’s typically used to display navigation on the left hand side, and a detail view on the right hand side.

Screenshot of finished UISplitViewController

So let’s start making this!

Starting From Scratch

Although you could use the “Master-Detail Application” template as a starting point, you’re going to start from scratch and pick the “Empty View Application” template. This is so you can get a better understanding of exactly how the UISplitView works. This knowledge will be helpful as you continue to use it in future projects (even if you choose the Master-Detail template in the future to save time!)

Select Empty Application template

So create a new Project, and choose the iOS\Application\Empty Application template, and name the project MathMonsters. Choose iPad in the Product dropdown – Universal Apps is a topic for a later tutorial. Finally, make sure Use Automatic Reference Counting is checked, and the others are unchecked, and finish creating the project.

You can go ahead and compile and run if you want, but all you’ll see is a blank screen at this point. You’ll want to create a Storyboard file – go to File\New\File… and select the iOS\User Interface\Storyboard template.

Add A Storyboard File

Select iPad as the Device Family, and name this Storyboard MainStoryboard_iPad.storyboard.

Next, select your project in the Project Navigator and scroll to the iPad Deployment Info section. Then in the Main Storyboard entry, enter the name of your Storyboard: MainStoryboard_iPad.

Set Storyboard as the Main Storyboard for the iPad

Open MainStoryboard_iPad.storyboard and drag a SplitViewController into the empty storyboard:

Adding a split view controller to the storyboard

This will add several elements to your storyboard:

  • A Split View Controller. This is the root view of your application – the split view that will contain the entire rest of the app. One helpful hint: If you go to the Attributes Inspector and set Simulated Metrics > Orientation to Landscape, it will actually look like a SplitViewController:
  • Simulated UISplitViewController in Storyboard

  • A Navigation Controller. This represents the UINavigationController that will be the root view of your Master View Controller (ie, the left pane of the SplitView). If you look in the Split View Controller, you’ll see the NavigationController has a Relationship Segue of “master view controller”. This allows you to create an entire navigation hierarchy in the Master View Controller without needing to affect the Detail View Controller at all.
  • A Table View Controller Root View Controller. This is the rootViewController of the UINavigationController represented by the Navigation Controller. This will eventually be the view controller with the list of monsters.
  • A View Controller. This will eventually display all the details of the monsters. If you look in the Split View Controller, you’ll see the View Controller has a Relationship Segue of “detail view controller”.

One final step before you can see this working in your app. Open AppDelegate.m and replace application:didFinishLaunchingWithOptions: with the following:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    return YES;

Here you removed the placeholder code the template added to create an empty UIWindow. You don’t need this anymore because you configured your app to use a Storyboard instead.

Build and run, and rotate your simulator to landscape. You should see an empty split view controller:

Empty split view controller

You’re going to want to have your own view controllers inside of here instead of these default ones, so let’s get started creating those.

Creating Custom View Controllers

You’re going to make two view controller placeholders to put inside your split view – a table view controller for the left hand side, and a “plain” view controller for the right hand side. Let’s start with the table view controller.

Go to File\New\File… and choose the iOS\Cocoa Touch\Objective-C class template. Name the class LeftViewController, make it a subclass of UITableViewController, and make sure both checkboxes are unchecked. Click Next and then Create.

Open LeftViewController.m, scroll down to numberOfSectionsInTableView: and replace the implementation as follows:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    return 1;

Then scroll down to tableView:numberOfRowsInSection: and replace the implementation with the following:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    return 10;

This way, you’ll just have 10 empty rows to look at when you test this thing out later.

Open MainStoryboard_iPad.storyboard and select the Table View Controller. Change the Class under CustomClass to LeftViewController using the Identity Inspector (3rd tab):

Changing Custom Class for LeftViewController

In addition, you need to make sure the Prototype Cell in the Table View is given a reuse identifier, or it will cause a crash when the Storyboard tries to load. Within the Left View Controller, select the Prototype Cell and then change the Identifier to “Cell” (which is already set as the default for UITableViewController subclasses in code), and the cell Style to “Basic”.

Setting Up The Prototype Cell

Now, you’ll create the View Controller for the right side. Go to File\New\File… and choose the iOS\CocoaTouch\Objective-C class template. Name the class RightViewController, make it a subclass of UIViewController, and make sure both checkboxes are unchecked. Click Next and then Create.

Open MainStoryboard_iPad.storyboard, and in the generic View Controller, select the View Controller object. Change the Class under CustomClass to RightViewController:

Right view controller

Then drag a label into the middle, and pin it to the Horizontal and Vertical centers of the container with AutoLayout.

Centering with Auto Layout

Change the text of the label to say say “Hello, World!” or something similar so you know it’s working when you test it out later. Note that if AutoLayout insists on pinning the width to something fixed that truncates what you’ve added, you can change the priority of that constraint to something less than 1000, and you should then be able to delete that constraint.

Build and run to try this out, and at this point you should see your custom view controllers:

Making Your Model

The next thing you need to do is define a model for the data you want to display. You don’t want to complicate things, so you’re going with a simple model with no data persistence.

First, make a class representing the Monsters you want to display. Go to File\New\File… and select the Cocoa Touch\Objective-C Class template. Name the class Monster, make it a subclass of NSObject. Click Next and then Create.

You’re just going to create a simple class with some instance variables with attributes about each Monster you want to display, and a couple of convenience methods for creating new monsters and accessing the image for the weapon each monster has. Replace Monster.h with the following contents:

#import <Foundation/Foundation.h>

typedef enum {
    Blowgun = 0,
} Weapon;

@interface Monster : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *description;
@property (nonatomic, strong) NSString *iconName;
@property (nonatomic, assign) Weapon weapon;

//Factory class method to create new monsters
+(Monster *)newMonsterWithName:(NSString *)name description:(NSString *)description 
  iconName:(NSString *)iconName weapon:(Weapon)weapon;

//Convenience instance method to get the UIImage representing the monster's weapon. 
-(UIImage *)weaponImage;

And Monster.m with the following:

#import "Monster.h"
@implementation Monster
+(Monster *)newMonsterWithName:(NSString *)name description:(NSString *)description 
  iconName:(NSString *)iconName weapon:(Weapon)weapon {
    Monster *monster = [[Monster alloc] init]; = name;
    monster.description = description;
    monster.iconName = iconName;
    monster.weapon = weapon;
    return monster;
-(UIImage *)weaponImage {

    switch (self.weapon) {
        case Blowgun:
            return [UIImage imageNamed:@"blowgun.png"];
        case Fire:
            return [UIImage imageNamed:@"fire.png"];
        case NinjaStar:
            return [UIImage imageNamed:@"ninjastar.png"];
        case Smoke:
            return [UIImage imageNamed:@"smoke.png"];
        case Sword:
            return [UIImage imageNamed:@"sword.png"];
            //Anything not named in the enum.
            return nil;

That’s it for defining the model – so let’s hook it up to your left side view!

Displaying the Monster List

Open up LeftViewController.h and add a new instance variable/property for a monsters array as follows:

#import <UIKit/UIKit.h>

@interface LeftViewController : UITableViewController

@property (nonatomic, strong) NSMutableArray *monsters;

Then add a few tweaks to LeftViewController.m to start using this new array:

// At top, under #import
#import "Monster.h"
// In numberOfRowsInSection, replace return 10 with:
return [_monsters count];
// In cellForRowAtIndexPath, after "Configure the cell..."
Monster *monster = _monsters[indexPath.row];
cell.textLabel.text =;

That’s it for the table view. Now let’s just populate it with some default monsters!

First, download some art from one of Ray’s Cocos2D games. Drag the folder containing those images into your MathMonsters folder in Xcode, making sure Copy items into destination group’s folder (if needed) is checked, then click Add.

After you add the images, open up LeftViewController.m and replace initWithStyle: with an initWithCoder: method setting up the array of monsters. Note that you are replacing initWithStyle: with initWithCoder: because this class is being loaded from a Storyboard.

-(id)initWithCoder:(NSCoder *)aDecoder
    if (self = [super initWithCoder:aDecoder]) {
        //Initialize the array of monsters for display.
        _monsters = [NSMutableArray array];
        //Create monster objects then add them to the array.
        [_monsters addObject:[Monster newMonsterWithName:@"Cat-Bot" description:@"MEE-OW" 
             iconName:@"meetcatbot.png" weapon:Sword]];
        [_monsters addObject:[Monster newMonsterWithName:@"Dog-Bot" description:@"BOW-WOW" 
             iconName:@"meetdogbot.png" weapon:Blowgun]];
        [_monsters addObject:[Monster newMonsterWithName:@"Explode-Bot" 
             description:@"Tick, tick, BOOM!" iconName:@"meetexplodebot.png" weapon:Smoke]];
        [_monsters addObject:[Monster newMonsterWithName:@"Fire-Bot" 
             description:@"Will Make You Steamed" iconName:@"meetfirebot.png" weapon:NinjaStar]];
        [_monsters addObject:[Monster newMonsterWithName:@"Ice-Bot" 
             description:@"Has A Chilling Effect" iconName:@"meeticebot.png" weapon:Fire]];
        [_monsters addObject:[Monster newMonsterWithName:@"Mini-Tomato-Bot" 
             description:@"Extremely Handsome" iconName:@"meetminitomatobot.png" weapon:NinjaStar]];        
    return self;

Compile and run the app, and if all goes well you should now see the list of bots on the left hand side!

Bots on left hand side of split view

Displaying Bot Details

Next, let’s set up the right hand side so you can see the details for a particular bot.

Open MainStoryboard_iPad.storyboard, go to the Right View Controller and delete the placeholder label you put down earlier.

Using the screenshot below as a guide, drag the following controls into the RightViewController’s view:

RightViewController Layout

  1. A 95×95 UIImageView for displaying the Monster’s image in the upper left hand corner.
  2. A UILabel aligned with the top of the UIIimageView with height 45 and font Helvetica Bold, size 36.
  3. Two UILabels underneath of height 31, with font Helvetica, size 24. One should be aligned with the bottom of the UIImageView, the other should be below it. They should have their leading edges aligned.
  4. A 70×70 IImageView for displaying the weapon image, aligned with the trailing edge of the “Preferred way to Kill” label.

Take care to set up the AutoLayout constraints carefully when you’re constructing this view:

  • Make sure the width and height of the ImageViews are pinned or they’ll stretch on rotation.
  • Make sure the width of the UILabels next to the monster’s image view is not pinned – otherwise your text may get truncated. Having the height pinned is fine (and probably necessary).
  • Try to use Align Edges as much as possible so the views’ positions depend on each other rather than on the superview’s position.

Getting AutoLayout using the proper constraints is especially important for the iPad since apps are required to handle autorotation properly for any orientation.

Note: Auto Layout can be a slippery devil! I highly recommend you check out our Beginning Auto Layout tutorial series if you run into any trouble.

Depending on how you lay out your view, you may get slightly different results, but here’s a glance at the constraints that worked when I built it out:

Autolayout Sample Constraints

Ok that’s it for Autolayout for now – let’s hook these views up to some properties. Add some IBOutlets to RightViewController.h as follows:

#import <UIKit/UIKit.h>

@class Monster;
@interface RightViewController : UIViewController

@property (nonatomic, strong) Monster *monster;
@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
@property (nonatomic, weak) IBOutlet UILabel *descriptionLabel;
@property (nonatomic, weak) IBOutlet UIImageView *iconImageView;
@property (nonatomic, weak) IBOutlet UIImageView *weaponImageView;


Here you added properties for the various UI elements you just added which need to dynamically change. You also added a property for the Monster object this view controller should display. Note that the IBOutlets are being retained weakly – they’re also being retained by the xib embedded in the Storyboard.

Then, use the following code in RightViewController.m to display the information from the monster:

#import "RightViewController.h"
#import "Monster.h"

@implementation RightViewController

- (void)viewDidLoad
    [self refreshUI];
    [super viewDidLoad];

-(void)setMonster:(Monster *)monster
    //Make sure you're not setting up the same monster.
    if (_monster != monster) {
        _monster = monster;
        //Update the UI to reflect the new monster on the iPad.
        [self refreshUI];

    _nameLabel.text =;
    _iconImageView.image = [UIImage imageNamed:_monster.iconName];
    _descriptionLabel.text = _monster.description;
    _weaponImageView.image = [_monster weaponImage];


Now, go back MainStoryboard_iPad.storyboard, right click the Right View Controller object to display the list of IBOutlets, then drag from the circle at the right of each item to the view to hook up the IBOutlets.

Hooking up IBOutlets to views

Ok, you’re close enough to test this out! Go back to AppDelegate.m and make the following tweaks:

//[Up top, under #import]
#import "LeftViewController.h"
#import "RightViewController.h"
//[Inside application:didFinishLaunchingWithOptions:
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;    
UINavigationController *leftNavController = [splitViewController.viewControllers objectAtIndex:0];
LeftViewController *leftViewController = (LeftViewController *)[leftNavController topViewController];
RightViewController *rightViewController = [splitViewController.viewControllers objectAtIndex:1];

Monster *firstMonster = [[leftViewController monsters] objectAtIndex:0];
[rightViewController setMonster:firstMonster];

A split view controller has a property named viewControllers array that has the left and right view controllers inside. The left view controller in your case is actually a navigation controller, so you get the top view controller from that to get your LeftViewController class.

Compile and run the app, and if all goes well you should see some monster details on the right:

Screenshot of finished UISplitViewController

Note that selecting the monsters on the right does absolutely nothing, however. That’s what you’ll do next!

Hooking Up The Left With the Right

There are many different strategies for how to best communicate between these two view controllers. In the Master-Detail Application template, Apple gives the left view controller a pointer to the right view controller, and the left view controller sets a property on the right view controller when a row gets selected. The right view controller overrides the property to update the view when the property is updated.

That works fine for simple applications where you only ever have one ViewController in the right pane, but you’re going to follow the approach suggested in the UISplitViewController class reference for more complex apps and use a delegate.

The basic idea is you’re going to define a protocol with a single method – “selectedMonster:”. Your right hand side will implement this method, and your left hand side will accept a delegate of an object which wants to know about this.

So let’s see this in code. First, create the file for the MonsterSelectionDelegate by going to File\New\File… and selecting the iOS\Cocoa Touch\Objective-C protocol template. Enter MonsterSelectionDelegate for the protocol, click Next, and then Create.

Replace the contents of MonsterSelectionDelegate.h with the following:

@class Monster;
@protocol MonsterSelectionDelegate <NSObject>
-(void)selectedMonster:(Monster *)newMonster;

Then, update LeftViewController.h to add a property for an object conforming to the delegate protocol:

#import "MonsterSelectionDelegate.h"

@interface LeftViewController : UITableViewController

@property (nonatomic, strong) NSMutableArray *monsters;
@property (nonatomic, assign) id<MonsterSelectionDelegate> delegate;


Basically, this means that the delegate property is required to be an object that has the selectedMonster: method implemented. That object will be responsible for handling what needs to happen within its view after the monster was selected.

So now, since you want the RightViewController to update when the monster is selected, you need to go into RightViewController and add a few things to make that work. First, in RightViewController.h declare that the RightViewController conforms to the protocol at the top of the file, underneath the Monster class declaration:

#import "MonsterSelectionDelegate.h"

@interface RightViewController : UIViewController <MonsterSelectionDelegate>

Then, at the bottom of RightViewController.m add the delegate method:

-(void)selectedMonster:(Monster *)newMonster
    [self setMonster:newMonster];

Next, you need to go into LeftViewController.m and update the didSelectRowAtIndexPath to notify the Monster selection delegate of the new monster.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    Monster *selectedMonster = [_monsters objectAtIndex:indexPath.row];
    if (_delegate) {
        [_delegate selectedMonster:selectedMonster];

And finally, go to AppDelegate.m. In didFinishLaunchingWithOptions right after the last line you added, add:

//Set the RightViewController as the left's delegate.
leftViewController.delegate = rightViewController;

That’s it! Compile and run the app, and you now should be able to select between the monsters like the following:

SplitView Selection Working

In case you’re wondering what the advantages are of using a delegate broken out into a separate file, note that there are no imports between the left side view and the right side view. This means that it would be a lot easier to re-use these views in other projects or swap out the views with other views, since you’ve been quite clear about what each one expects.

So far so good with split views! Except there’s one problem left – if you rotate the simulator to vertical orientation, you can’t see the list of monsters anymore, so you have no way to switch between them. Luckily, Apple has given us an easy way to remedy this – the Split View Controller delegate.

Setting the Split View Controller Delegate

Just like many clases in iOS, the Split View Controller can notify you when interesting things occur. All you need to do is register your class as the delegate.

To do this, open RightViewController.h and mark the class as implementing UISplitViewControllerDelegate:

@interface RightViewController : UIViewController <MonsterSelectionDelegate, UISplitViewControllerDelegate>

Then switch to RightViewController.m and add some stub implementations for a few of the delegate methods so you can see them working:

-(void)splitViewController:(UISplitViewController *)svc 
    willHideViewController:(UIViewController *)aViewController 
    withBarButtonItem:(UIBarButtonItem *)barButtonItem  
    forPopoverController:(UIPopoverController *)pc
    NSLog(@"Will hide left side");
-(void)splitViewController:(UISplitViewController *)svc 
    willShowViewController:(UIViewController *)aViewController 
    invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
    NSLog(@"Will show left side");

Finally, switch to AppDelegate.m and add this line to the bottom:

splitViewController.delegate = rightViewController;

Build and run, and rotate your simulator from landscape to portrait a few times. You should notice some log messages in the console like this:

2013-02-07 11:25:36.734 MathMonsters[24551:c07] Will hide left side
2013-02-07 11:25:45.979 MathMonsters[24551:c07] Will show left side

Now that you’re getting notifications when the split view controller is showing and hiding the left hand side view controller, you just need to add some code to add a button into the toolbar so the user can take a peek at the left hand side view controller, even when the iPad is in portrait mode.

Adding a Toolbar and Popover List

You may have noticed that the delegate methods pass you a handy UIBarButtonItem you can add to the toolbar to use to display the left hand side in a popover. All you need to do is add this button to your toolbar or navigation bar.

So let’s give this a try. Open MainStoryboard_iPad.storyboard, find the Right View Controller, and drag a UINavigationBar to the top of the view. You will probably have to move everything else down a bit – you should be able to preserve most of your existing constraints.

Pin the Vertical Spacing of the navigation bar and the iconImageView to make sure the other views will stay below the toolbar properly on rotation.

Then add two properties RightViewController.h:

@property (nonatomic, weak) IBOutlet UINavigationItem *navBarItem;
@property (nonatomic, strong) UIPopoverController *popover;

And the following to RightViewController.m’s selectedMonster: method:

//Dismisses the popover if it's showing.
if (_popover != nil) {
     [_popover dismissPopoverAnimated:YES];

Now add the UISplitViewDelegate protocol methods for responding to showing and hiding the LeftViewController to RightViewController.m below the MonsterSelectionDelegate method:

#pragma mark - UISplitViewDelegate methods
-(void)splitViewController:(UISplitViewController *)svc 
    willHideViewController:(UIViewController *)aViewController 
    withBarButtonItem:(UIBarButtonItem *)barButtonItem  
    forPopoverController:(UIPopoverController *)pc
    //Grab a reference to the popover
    self.popover = pc;
    //Set the title of the bar button item
    barButtonItem.title = @"Monsters";
    //Set the bar button item as the Nav Bar's leftBarButtonItem
    [_navBarItem setLeftBarButtonItem:barButtonItem animated:YES];

-(void)splitViewController:(UISplitViewController *)svc 
    willShowViewController:(UIViewController *)aViewController 
    invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
    //Remove the barButtonItem.
    [_navBarItem setLeftBarButtonItem:nil animated:YES];

    //Nil out the pointer to the popover.
    _popover = nil;

Finally, go back to the Right View Controller the Storyboard and hook up navBarItem IBOutlet to the UINavigationBar’s NavigationItem object (and while you’re in there, you can also delete the NavigationItem’s title of “Title”). That’s it! Compile and run the project and now when you rotate, a button will appear on your toolbar that you can tap for a popover as follows:

UISplitViewDelegate Popover

Note: If you want to the button showing/hiding and the sidebar coming into and out of the frame in action on the simulator, you can go to the Debug menu and select “Toggle slow animations”, and you can watch the view button show and hide automatically.

Normal UIPopoverViewController type

Standard UIPopover.

Note that the popover that shows for a UISplitViewController is a little unique – it slides out like a drawer. Most other popovers you deal with will be more like those in part 2 of this tutorial, where there’s a border and an arrow pointing at a button.

Using a UINavigationController Instead

In this project, you’re using a custom view with a static UINavigationBar on the right hand side. However in your projects, you might want to use a UINavigationController on the right side instead, to allow you to navigate further views on the right. How can you set it up so that the bar button item goes into the UINavigationController’s toolbar?

To do this, all you need to do is get a reference to your UIViewController’s navigationItem property, and in the willHideViewController method simply set the left bar button item as you are in your current version:

UINavigationItem *navItem = [self navigationItem];
[navItem setLeftBarButtonItem:barButtonItem animated:YES];

Note that if your UIViewController is not in a UINavigationController’s View Controller stack, navigationItem will be nil.

Where To Go From Here?

Here’s an example project with all of the code you’ve developed so far.

So – that’s how you can use UISplitViewController, from the ground up. Note in practice you’re likely to just use the Master-Detail template to save time – but now you should know much better how it works.

Continue on with the next part of the series, where you’ll learn how to use popovers on the iPad!

Or if you have any questions or comments, please join the forum discussion below!

Add a rating for this content