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. By Ellen Shapiro.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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>
@required
-(void)selectedMonster:(Monster *)newMonster;
@end

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;

@end

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.

Standard UIPopover.

Normal UIPopoverViewController type

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.

Contributors

Over 300 content creators. Join our team.