Tag Archives: UIScreen

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:





How to load UIStoryboards depending on screen height in iOS

A while ago I’ve written an article about how to load different storyboards depending on the screen size of an iOS device. Back in those days (2013) it was all a bit simpler than it is today, and I looked into it mainly because I loathed Auto Layout so much.

I felt it was time for an update, so here it is!

Things haven’t gotten any easier in iOS, because currently we have the following 5 screen sizes to deal with:

  • iPhone 6 Plus: 736×414 @3x
  • iPhone 6: 667×375 @3x
  • iPhone 5s: 568×320 @2x
  • iPhone 4s: 480×320 @2x
  • all iPads: 1024×768 @1x / @2x

It’s very difficult to make a UI look nice on all devices with a single UIStoryboard, and in the above video I’m showing you an alternative: design a completely different storyboard for each screen size.

The upkeep of such an app will be a little more complex, but it puts us in full control of the user experience, and not some compromise that sounds good in the Apple presentation (and sucks in reality).

In principle, the following steps are involved:

  • design various storyboards
  • detect the current device’s screen height
  • load the appropriate storyboard
  • make it “key and visible”

Detecting the screen size

If your app is set to “auto-rotate” (i.e. both portrait and landscape modes, or portrait only), the screen height will detect the longer side of the screen. This is true even if the app is started in landscape mode. Determining screen height can be done like this:

int screenHeight = [UIScreen mainScreen].bounds.size.height;
NSLog(@"Screen Height is %i", screenHeight);

Note that if you set your app to “landscape only” mode, the height parameter will return the shorter side of the screen – in which case, bounds.size.width to determine the longer portion of the screen. Thanks to Johan Grip for bringing this to my attention!

iOS 7 compatibility

Note that the screen size is orientation dependant since iOS 8 – previous versions did not take this into a account. If you must support iOS 7 and earlier it gets a lot more complicated (and I won’t cover this here – sorry).

However, this Stack Overflow discussion may help you in that case: http://stackoverflow.com/questions/24150359/is-uiscreen-mainscreen-bounds-size-becoming-orientation-dependent-in-ios8

Loading the correct UIStoryboard

With this handy integer in place, we can build a switch statement to react according to the screen height. I’m using the following method that returns my desired storyboard in my AppDelegate implementation file.

If you’re not worried about each single size, feel free to write a shorter if/then/else type method.

 

- (UIStoryboard *)grabStoryboard {
    
    // determine screen size
    int screenHeight = [UIScreen mainScreen].bounds.size.height;
    UIStoryboard *storyboard;
    
    switch (screenHeight) {
            
            // iPhone 4s
        case 480:
            storyboard = [UIStoryboard storyboardWithName:@"Main-4s" bundle:nil];
            break;
            
            // iPhone 5s
            case 568:
            storyboard = [UIStoryboard storyboardWithName:@"Main-5s" bundle:nil];
            break;
            
            // iPhone 6
            case 667:
            storyboard = [UIStoryboard storyboardWithName:@"Main-6" bundle:nil];
            break;
            
            // iPhone 6 Plus
            case 736:
            storyboard = [UIStoryboard storyboardWithName:@"Main-6-Plus" bundle:nil];
            break;
            
        default:
            // it's an iPad
            storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
            break;
    }
    
    return storyboard;
}

 

Displaying the storyboard

Inside our AppDelegate method didFinishLaunchingWithOptions, we’ll call the above method and grab the storyboard we need. To make it show up, we need to load it as the window’s root view controller and declare it “key and visible”. This is akin to the old-style way of making things appear on our iOS screens.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // grab correct storyboard depending on screen height
    UIStoryboard *storyboard = [self grabStoryboard];
    
    // display storyboard
    self.window.rootViewController = [storyboard instantiateInitialViewController];
    [self.window makeKeyAndVisible];
    
    return YES;
}

Note that using this approach will override whatever storyboard is declared in your target (under General – Deployment Info – Main Interface).

 

Demo Project

I’ve updated this project on GitHub, it shows exactly what I’m building in the screencast. Play and enjoy 🙂