iPad Tutorial for iOS: How To Port an iPhone Application to the iPad

An iPad tutorial that shows you how to port an iPhone application to the iPad. By Ray Wenderlich.

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

UISplitViewController Integration

UISplitViewControllers are designed so that you navigate to an item on the left hand side, and then see the details of the item on the right hand side.

This would be perfect for our app! We can put the list of board games on the left, and put the board game details on the right.

So let’s go ahead and integrate a UISplitViewController into our project. Open up MainWindow-iPad.xib, and drag a Split View Controller into the window, and delete the old Navigation Controller.

Expand the Split View Controller tree until you find the Table View Controller. In the Inspector, go to the fourth tab and set the Class to PortMeGameListController.

Now we have to set up the right hand side. Let’s think about this a minute. When the user taps “Rate This”, we currently push another view controller onto the stack. This means that we need the right side view controller also to be a UINavigationController (at least for now until we change that).

So drag a Navigation Controller on top of the View Controller for the right hand side, dig down and set the root view controller to “PortMeGameDetailsController.” When you’re done it should look like this:

UISplitView XIB settings

Ok now we need to hook this up to the code. Currently, the Application Delegate is set up to add the navController property as a subview to the main window. However, for the iPad, we want it to add the split view controller to the window instead.

This means that we need an outlet for the split view controller. We might be tempted to just declare it as normal – however keep in mind this app needs to work on both the iPhone (running iPhone OS 3.0-3.1.3) and the iPad (running iPhone OS 3.2). iPhone OS 3.2 is iPad only btw.

So this means that the older OS versions will not have the UISplitViewController class, and trying to declare variables of that type will cause problems. Therefore, we need to be much more careful about how we use those clasess, and use runtime checks for new symbols.

The easiest way to see how to do this is to try it out. Open up PortMeAppDelegate.h and add the following code:

// In the class interface
id _splitViewController;

// Afterwards
@property (nonatomic, retain) IBOutlet id splitViewController;

Here we declare the SplitViewController as a generic id so we can load up the class at runtime and avoid problems on the 3.0 OS.

Then add the following to PortMeAppDelegate.m:

// In synthesize section
@synthesize splitViewController = _splitViewController;

// In didFinishLaunchingWithOptions, replace window addSubview line with:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {      
    UIView *view = [_splitViewController view];
    [window addSubview:view];
} else {
    [window addSubview:_navController.view];	
}

// In dealloc
self.splitViewController = nil;

The first line is the test you can use to see if your code is running on the iPad or not. If it is running on the iPad, we want to add the split view controller’s view as a subview of our window.

Since we have stored the split view as a generic object (rather than as the UISplitViewController class), we need to get the view by sending a message to it (rather than using dot notation).

That’s it for code! Last thing is to go back to MainWindow-iPad.xib and Control-drag from “Port Me App Delegate” to the “Split View Controller” and connect it to the “splitViewController” outlet, and Control-drag from “Port Me App Delegate” to the “Port Me Game List Controller” and connect it to the “gameListController” outlet.

Make sure you save the XIB, then compile and run the app, and switch to landscape mode. The list of games shows up OK on the left, but when you tap a game it shows up in the same navigation controller instead of on the right hand side:

Left side incorrectly showing detail view

So let’s fix that next!

Linking up the Detail View

As mentioned in the UISplitView iPad tutorial, there are many good ways to hook the left and the right sides of a split view together, but one approach that works particularly well is delegation.

So we’ll follow the same approach we did in that iPad tutorial and set up a protocol for “game selected” that the detail view will implemenet to refresh the view.

Actually, this is a good point where you can practice doing this on your own and make sure you remember how to do it. If you successfully implement it on your own, just skip to the next section. Otherwise, you can keep following along!

If you choose to continue following along, go to File\New, choose Objective-C class, make sure “Subclass of” is “NSObject”, and click “Next”. Name the file “GameSelectionDelegate” and click “Finish”.

Replace GameSelectionDelegate.h with the following:

#import <Foundation/Foundation.h>

@class Game;

@protocol GameSelectionDelegate
- (void)gameSelectionChanged:(Game *)curSelection;
@end

Then delete GameSelectionDelegate.m, since we don’t need it for a protocol.

Now, let’s modify the PortMeGameListController to take a GameSelectionDelegate. And add the following to PortMeGameListController.h:

// At top, under #import
#import "GameSelectionDelegate.h"
 
// Inside LeftViewController
id<GameSelectionDelegate> _delegate;
 
// Under @property
@property (nonatomic, assign) IBOutlet id<GameSelectionDelegate> delegate;

And add the following to PortMeGameListController.h:

// Under @implementation
@synthesize delegate = _delegate;
 
// Inside didSelectRowAtIndexPath, replace navigation push line with:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    if (_delegate != nil) {            
        [_delegate gameSelectionChanged:game];
    }
} else {
    [self.navigationController pushViewController:_detailsController animated:YES];
}
 
// Inside dealloc
self.delegate = nil;

Then, we’ll modify PortMeGameDetailsController to implement the delegate. Make the following changes to PortMeGameDetailsController.h:

// At top, under #import
#import "GameSelectionDelegate.h"
 
// Class declaration line
@interface PortMeGameDetailsController : UIViewController <GameSelectionDelegate> {

Then add the following to PortMeGameDetailsController.m:

// Replace viewWillAppear with the following:
- (void)refresh {
    _nameLabel.text = _game.name;
    _typeLabel.text = _game.type;
    _descrView.text = _game.descr;
    _ratingLabel.text = [NSString 
        stringWithFormat:@"Your rating: %@", _game.rating];
}

- (void) viewWillAppear:(BOOL)animated {
    [self refresh];
}

- (void)gameSelectionChanged:(Game *)curSelection {
    self.game = curSelection;
    [self refresh];
}

And for the final step, we can actually connect the delegate using Interface Builder since we marked the delegate as an IBOutlet. Open up MainWindow-iPad.xib and Control-drag from “Port Me Game List Controller” to “Port Me Game Details Controller” and connect it to the delegate outlet.

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

Right side hooked up with left