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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
iPad Tutorial for iOS: How To Port an iPhone Application to the iPad
30 mins
Adding a Popover List
Just like we did in the UISplitView iPad tutorial, it’s standard practice to have a way to bring up the left hand side when you’re in portrait mode by tapping a button in the toolbar.
Although what we are about to do is similar to the way we did in the last iPad tutorial, there are a few changes due to this being a Universal app, and since the right side is a navigation controller rather than a view with a toolbar, so you may want to keep following along here.
Make the following changes to PortMeGameDetailsController.h:
// Add UISplitViewControllerDelegate to the list of protocols
@interface PortMeGameDetailsController : UIViewController
<GameSelectionDelegate, UISplitViewControllerDelegate> {
// Inside the class definition
id _popover;
// In the property section
@property (nonatomic, retain) id popover;
This is similar to the way we did in the past iPad tutorial, except we declare the UIPopoverController as a generic object to avoid problems on the 3.0-3.1.3 OS.
Then add the following to PortMeGameDetailsController.m:
// In synthesize section
@synthesize popover = _popover;
// In dealloc and viewDidUnload
self.popover = nil;
// In gameSelectionChanged
if (_popover != nil) {
[_popover dismissPopoverAnimated:YES];
}
// New functions
- (void)splitViewController: (UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController: (UIPopoverController*)pc {
barButtonItem.title = @"Sidebar";
UINavigationItem *navItem = [self navigationItem];
[navItem setLeftBarButtonItem:barButtonItem animated:YES];
self.popover = pc;
}
- (void)splitViewController: (UISplitViewController*)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
UINavigationItem *navItem = [self navigationItem];
[navItem setLeftBarButtonItem:nil animated:YES];
self.popover = nil;
}
Then add the following to PortMeGameListController.m to make the popover be a bit smaller rather than the full height of the screen:
// In viewDidLoad
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self setContentSizeForViewInPopover:CGSizeMake(320.0, 300.0)];
}
Note we have to be careful to only run this if we’re on the iPad (and use message passing rather than dot notation) since we may be running on the 3.0 OS.
One last thing – go back to MainWindow-iPad.xib and control-drag from “Split View Controller” to “Port Me Game Deetails Controller” to set the right view controller as the delegate of the split view controller.
Compile and run the app, and if all goes well you should have an item on your nav controller bar that you can tap to bring up the list of board games!
Using UIPopoverController
There’s still another area in our app where we’re thinking too much in iPhone terms – the rate button. Rather than going to a completely separate screen, this would be much better served by using a UIPopoverController on the iPad.
This is similar to the method we used in the UIPopoverController iPad tutorial but again with some slight differences.
Add the following to PortMeGameDetailsController.h:
// Inside class declaration
id _ratingPopover;
// In property section
@property (nonatomic, retain) id ratingPopover;
Again note the use of the generic objects here since this is a Universal app.
And the following to PortMeGameDetailsController.m:
// In synthesize section
@synthesize ratingPopover = _ratingPopover;
// In dealloc AND viewDidUnload
self.ratingPopover = nil;
// In rateTapped, replace pushViewController with the following:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIButton *button = (UIButton *)sender;
if (_ratingPopover == nil) {
Class classPopoverController = NSClassFromString(@"UIPopoverController");
if (classPopoverController) {
self.ratingPopover = [[[classPopoverController alloc]
initWithContentViewController:_ratingController] autorelease];
}
}
[_ratingPopover presentPopoverFromRect:button.frame inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
} else {
[self.navigationController pushViewController:_ratingController animated:YES];
}
Here we again switch based on whether we’re running on the iPad or not, and either present a popover or push onto the navigation controller as usual.
However note we have to create the popover controller by looking up the class name from a string, and then constructing it. This is again a case where we can’t use the actual class name since we may be running on a 3.0 device.
We also use a slightly different method to present the popover here, which lets us specify a rectangle which the popover will point to with an arrow.
Next switch over to PortMeGameRatingController.m and add the following:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self setContentSizeForViewInPopover:CGSizeMake(320.0, 300.0)];
}
Compile and run the app, and if you select a game and tap rate you should see the following:
Getting a Better Detail View
So far, our port is coming along quite well, and is behaving much more iPad like. However, our detail view could still use some work.
First, the image in the background was actually made for the iPhone, and is just being scaled to the larger screen. So it looks a bit grainy at the larger resolution.
Secondly, we could make better use of the increased screen real estate by making some of the text bigger, or moving around some of the labels to make better use of the space.
When you start to get a lot of changes you’d like to make to the view like this, you COULD do everything programatically by modifying the UI elements in code and switching on the UI_USER_INTERFACE_IDIOM(), but it’s often easier to just make a custom view XIB for the iPad.
So let’s give that a shot! Open up PortMeGameDetailsController.xib, and click “File\Create iPad Version Using Autosizing Masks”. It will create an Untitled XIB – save the XIB in the project folder and name it “PortMeGameDetailsController-iPad.xib”.
Then download a copy of a higher resolution background image and set the UIImageView’s image to “bg-iPad.jpg”. (Image credit: szajmon).
Then, have some fun improving the interface a bit. I’d recommend the moving the board game type to the right of board game name, aligned right. Make sure to fix the autosizing masks appropriately! Also, you should probably improve the font size of everything.
Save the XIB. The last step is to make sure that the iPad XIB is the one that is loaded. Open up MainWindow-iPad.xib, select the “Port Me Game Details Controller”, and go to the first tab of the Inspector. Set the NIB name to “PortMeGameDetailsController-iPad.xib”.
Save the XIB, compile and run the project. If all goes well you should see the new view like the following:
Testing on the iPhone
Ok, our app is looking pretty good on the iPad. The next step is to try it again on the iPhone and make sure we didn’t break anything!
Erm, but we have a small problem. By default the simulator launches directly into the iPad simulator (not the iPhone simulator).
So what do we do? The workaround I used was to just test on the device. However if that is annoying, Noel from Games from Within has written a nice post about Universal Apps that includes a workaround to run in the iPhone simulator.
Switch to “Device – 3.2” and try to compile and run the app. Oops – we get an error message like the following:
That’s OK. We can fix this by a setting in our Target Info. Basicaly, even though we’re compiling against the 3.2 SDK, we can deploy to a different SDK.
This is why we were being so careful about using all of those runtime checks – we have the 3.2 headers available to us but maybe not the actual code when we run on the device.
So go to Target Info and set the iPhone OS Deployment Target to iPhone OS 3.0 as follows:
Try running again and this time it should show up just fine on our iPhone – and since we were careful about runtime checks, everything works like normal! :]