How to load different Storyboards for different versions of iOS in Xcode 5

No matter how hard I’ve tried to tweak my Storyboard, there are always areas that only look well on one particular version of iOS. There is no “one Storyboard fits all” approach.

The easiest option is to have one Storyboard for iOS 6 and another for iOS 7 – then everybody’s happy. Question is, how do we tell our app which one to load depending on the iOS version our device is running?

Here’s how: AppDelegate.m to the rescue! Let’s determine it in applicationDidFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // load storyboard depending on iOS version

    UIStoryboard *storyboard;
    
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
        // Load resources for iOS 6.1 or earlier
        storyboard = [UIStoryboard storyboardWithName:@"OldStoryboard" bundle:nil];
        
    } else {
        // Load resources for iOS 7 or later
        storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
        
    }
    
    // show the storyboard
    self.window.rootViewController = [storyboard instantiateInitialViewController];
    [self.window makeKeyAndVisible];
    
    return YES;
}

This will override whichever Storyboard is defined in the target (under Deployment Info, Main Interface).





8 thoughts on “How to load different Storyboards for different versions of iOS in Xcode 5

  1. Wondering if this same code can be edited to allow the user to save the settings (preference) of which storyboard they prefer. I have an app that uses an Imperial Storyboard and a Metric Storyboard. At startup the user actually sees a Startup Storyboard and then they select Imperial or Metric… That works fine…

    I have an option to allow them to save their default unit of measure, so they don’t have to select Imperial or Metric every time they start the app again using NSUserDefaults… Those defaults too save properly.

    My problem is, that I cannot tell in the AppDelegate file that if the user saved Imperial, how to only display the Imperial Storyboard on next entry, and of course, if the user selected Metric, how to load only that Storyboard.

    So while I came close to editing your sample (thank you) I am having a bit of trouble having the AppDelegate file handle the selection.

    If you have any advice much appreciated…

    Here is some sample code… (not working) no errors, just not selecting the saved Storyboard from the user preferences as every time the app starts up, it always goes to the Start Storyboard and not the Imperial or Metric Storyboard…

    ***Code Below *** in AppDelegate.m

    - (UIStoryboard *)grabStoryboard {
        
        UIStoryboard *storyboard;
        
        // detect the user default preference unit of measure
        
        NSUserDefaults *defaultimperial = [NSUserDefaults standardUserDefaults];
        if (![defaultimperial objectForKey:@"unitSwitchImperial"]) {
            [defaultimperial setBool:YES forKey:@"unitSwitchImperial"];
            storyboard = [UIStoryboard storyboardWithName:@"iPhoneImperial" bundle:nil];
            
             NSLog(@"Imperial Measurement Storyboard");
        } else {
            
            NSUserDefaults *defaultmetric = [NSUserDefaults standardUserDefaults];
            if (![defaultmetric objectForKey:@"unitSwitchMetric"]) {
                [defaultmetric setBool:YES forKey:@"unitSwitchMetric"];
            }
            storyboard = [UIStoryboard storyboardWithName:@"start" bundle:nil];
             NSLog(@"Start Storyboard");
        }
        
        return storyboard;
    }
    

    Then in the following…

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    
    NSUserDefaults *defaultimperial = [NSUserDefaults standardUserDefaults];
            if (![defaultimperial objectForKey:@"unitSwitchImperial"]) {
                [defaultimperial setBool:YES forKey:@"unitSwitchImperial"];
                
            }
    
    UIStoryboard *storyboard = [self grabStoryboard];
    
           // show the storyboard
            self.window.rootViewController = [storyboard instantiateInitialViewController];
            [self.window makeKeyAndVisible];
    
    

    ***

    so again, there are no errors, only that the app ALWAYS starts at the Start Storyboard, even though the user as selected the Imperial storyboard to start at the next time they enter the app…

    Any help much appreciated…

    John

    1. Hi John, your logic is sound and it’s definitely possible. Your code is correct too as far as I can see, there’s just one small thing when it comes to checking the user defaults.

      In applicationDidFinishLaunching, you’re saying “if the BOOL isn’t set, then set it”. This overrides the value every time the app starts, even if the user has chosen metric. What you want to do instead, “if the BOOL is set, load the imperial storyboard. And if the BOOL is not set, then load the metric storyboard”.

      In code this could look something like this:

      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
      {
          UIStoryboard *storyboard = nil;
          NSUserDefaults *defaultimperial = [NSUserDefaults standardUserDefaults];
          
          if ([defaultimperial boolForKey:@"unitSwitchImperial"]) {
              // load imperial storyboard
              storyboard = [self grabStoryboardImperial];
              
          } else {
              // load metric storyboard
              storyboard = [self grabStoryboardMetric];
          }
          
          // show the storyboard
          self.window.rootViewController = [storyboard instantiateInitialViewController];
          [self.window makeKeyAndVisible];
          
          return YES;
      }
      

      Here’s a quick demo project: https://github.com/versluis/Pick-a-Storyboard/

      Hope this helps!

      1. So using your method, I edited to show the Start Controller to allow the user to show the options, if they do not set them, at all… just a simple else if added… code below for reference…

        UIStoryboard *storyboard = nil;
                NSUserDefaults *defaultimperial = [NSUserDefaults standardUserDefaults];
                NSUserDefaults *defaultmetric = [NSUserDefaults standardUserDefaults];
                
                if ([defaultimperial boolForKey:@"unitSwitchImperial"])
                {
                    // load imperial storyboard
                    storyboard = [self grabStoryboardImperial];
                }
                else if ([defaultmetric boolForKey:@"unitSwitchMetric"])
                {
                    // load metric storyboard
                    storyboard = [self grabStoryboardMetric];
                }
                 else
                {
                    // load start storyboard
                    storyboard = [self grabStoryboardStart];
                }
                // show the storyboard
                self.window.rootViewController = [storyboard instantiateInitialViewController];
                [self.window makeKeyAndVisible];
        

        Then added the extra storyboard like this…

        - (UIStoryboard*)grabStoryboardMetric {
            
            return [UIStoryboard storyboardWithName:@"iPhoneMetric" bundle:nil];
        }
        
        - (UIStoryboard*)grabStoryboardImperial {
            
            return [UIStoryboard storyboardWithName:@"iPhoneImperial" bundle:nil];
        }
        
        - (UIStoryboard*)grabStoryboardStart {
            
            return [UIStoryboard storyboardWithName:@"start" bundle:nil];
        }
        

        Again, thanks for all your help!

    2. If anyone is interested… I solved this using the following code…

      if([defaultimperial boolForKey:@"unitSwitchImperial"])
              {
                  /* Executes when the boolean expression is true */
                  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhoneImperial" bundle:[NSBundle mainBundle]];
                  UIViewController *vc =[storyboard instantiateInitialViewController];
                  
                  // Set root view controller and make windows visible
                  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
                  self.window.rootViewController = vc;
                  [self.window makeKeyAndVisible];
                  NSLog(@"Imperial Storyboard");
              }
              
              else if([defaultmetric boolForKey:@"unitSwitchMetric"])
              {
                  /* Executes when the boolean expression is true */
                  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhoneMetric" bundle:[NSBundle mainBundle]];
                  UIViewController *vc =[storyboard instantiateInitialViewController];
                  
                  // Set root view controller and make windows visible
                  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
                  self.window.rootViewController = vc;
                  [self.window makeKeyAndVisible];
                  NSLog(@"Metric Storyboard");
              }
              
              else
              
              {
                  /* executes when the none of the above condition is true */
                  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"start" bundle:[NSBundle mainBundle]];
                  UIViewController *vc =[storyboard instantiateInitialViewController];
                  
                  // Set root view controller and make windows visible
                  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
                  self.window.rootViewController = vc;
                  [self.window makeKeyAndVisible];
                  NSLog(@"Start Storyboard");
                  
              }
      

Leave a Reply