How to load a different storyboard for different iPad Sizes

Since the introduction of the iPad Pro range, we now have to deal with three distinct iPad screen sizes. And although the 10.5″ and 9.7″ are very similar in size, the giant iPad Pro 12.9″ easily looks shockingly bad when used with a storyboard that otherwise looks handsome on the “smaller” iPad screens.

So what is a dev to do? Prepare a completely different storyboard for an iPad Pro 12.9″ of course! I’ve shown in the past how to do this for different phone sizes, so here’s how to do it for different iPad sizes. This approach will work with iOS 8 and above.

Determining the Screen Height

Sadly Apple have not implemented a new UIUserInterfaceIdiom property we could question. Every iPad device will only identify itself as an iPad rather than an iPad Pro model. But I guess this wouldn’t help us much anyway, since we have three screen sizes as of 2017, with god only knows how many more in the pipeline.

Hence, we need to determine what screen height we’re dealing with. To do this reliably though, we must also know if the user is holding the device in portrait or landscape mode when our app starts, otherwise “height” might be interpreted differently.

Lucky for us, there is a trait collection property we can use to question this, called fixedCoordinateSpace. We can call it on our UIScreen class. Here’s how:

int height = [UIScreen mainScreen].fixedCoordinateSpace.bounds.size.height;
NSLog(@"The fixed height is %i", height);

This test shall be at the heart of our endeavours. When derived like this, height will always deliver the “portrait up” height of our device, regardless if the app starts in portrait or landscape mode.

Now we’ll add another test to it, namely if the device we’re testing is in fact an iPad. If it is, we’ll compare the height parameter to the height of a 10.5″ iPad (which is 1024 pixels, just like it would be for a 9.7″ device). If it’s larger, we’re dealing with a 12.9″ device. And if it’s not an iPad at all, we’ll load an iPhone storyboard.

Here’s how we might do that:

- (UIStoryboard *)grabStoryboard {

    UIStoryboard *storyboard;
    
    // detect screen height
    int height = [UIScreen mainScreen].fixedCoordinateSpace.bounds.size.height;
    NSLog(@"The fixed height is %i", height);
    
    // determine if this is an iPad
    if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
        
        // it's an iPad 10.5" or lower
        if (height <= 1024) {
            
            storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil];
        } else {
            // it's an iPad Pro 12.9"
            storyboard = [UIStoryboard storyboardWithName:@"iPad-Pro" bundle:nil];
        }
        
    } else {
        // not an iPad, load regular storyboard
        storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    }
    
    return  storyboard;
}

In our project we have three storyboards: Main, iPad and iPad-Pro. Each of these will now be returned by this method depending on their screen height.

Displaying our Storyboard

Now that we have a reference to the one we need, let’s load it in our AppDelegate. We’ll do that just before returning YES in the didFinishLaunchingWithOptons method:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // grab and show the storyboard
    UIStoryboard *storyboard = [self grabStoryboard];
    self.window.rootViewController = [storyboard instantiateInitialViewController];
    [self.window makeKeyAndVisible];
    
    return YES;
}

It is irrelevant which storyboard is set as the main storyboard in the iOS target, it will be overridden by this method’s last two calls.

Demo Project

I’ve got a demo project on GitHub to demonstrate this, feel free to check it out and tinker with it:





Leave a Reply