iOS: Added support for iOS 8 LaunchScreen NIBs.
authorEric Wing <ewing . public |-at-| gmail . com>
Sun, 02 Nov 2014 20:55:13 -0800
changeset 92430895ce420b99
parent 9242 f4d353bd5d16
child 9244 9f8962b9b09e
iOS: Added support for iOS 8 LaunchScreen NIBs.

iOS 8 introduces LaunchScreen NIBs which use autolayout to handle all devices and orientations with a single NIB instead of multiple launch images. This is also the only way to get the App Store badge "Optimized for iPhone 6 and iPhone 6 Plus". So if the application is running on iOS 8 or greater AND has specified a LaunchScreen in their Info.plist, this patch will use the NIB as the launch screen. Otherwise, the code falls back to the legacy code path.

Note: Upon audit of the legacy path, it appears that it does not properly handle the UILaunchImages Info.plist convention. I've added comments inline to the code about this. However, in about a year from now, nobody is going to care about this path since everybody should be using LaunchScreen NIBs.
src/video/uikit/SDL_uikitappdelegate.m
     1.1 --- a/src/video/uikit/SDL_uikitappdelegate.m	Thu Nov 27 09:55:34 2014 -0500
     1.2 +++ b/src/video/uikit/SDL_uikitappdelegate.m	Sun Nov 02 20:55:13 2014 -0800
     1.3 @@ -75,6 +75,62 @@
     1.4      [UIApplication sharedApplication].idleTimerDisabled = disable;
     1.5  }
     1.6  
     1.7 +
     1.8 +@interface SDL_launchscreenviewcontroller : UIViewController {
     1.9 +	
    1.10 +}
    1.11 +
    1.12 +@end
    1.13 +
    1.14 +@implementation SDL_launchscreenviewcontroller
    1.15 +
    1.16 +- (id)init
    1.17 +{
    1.18 +    self = [super init];
    1.19 +    if (self == nil) {
    1.20 +        return nil;
    1.21 +    }
    1.22 +
    1.23 +    NSString* launch_screen_name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
    1.24 +
    1.25 +    if(launch_screen_name) {
    1.26 +        // TODO: If the NIB is not in the bundle, this will throw an exception. We might consider a pre-emptive check, but returning a useless viewcontroller isn't helpful and the check should be outside.
    1.27 +        UIView* launch_screen = [[[NSBundle mainBundle] loadNibNamed:launch_screen_name owner:self options:nil] objectAtIndex:0];
    1.28 +        CGSize size = [UIScreen mainScreen].bounds.size;
    1.29 +        
    1.30 +        CGRect bounds = CGRectMake(0, 0, size.width, size.height);
    1.31 +        
    1.32 +        [launch_screen setFrame:bounds];
    1.33 +        [self setView:launch_screen];
    1.34 +        [launch_screen release];
    1.35 +    }
    1.36 +
    1.37 +    
    1.38 +
    1.39 +
    1.40 +    return self;
    1.41 +}
    1.42 +
    1.43 +- (NSUInteger)supportedInterfaceOrientations
    1.44 +{
    1.45 +    NSUInteger orientationMask = UIInterfaceOrientationMaskAll;
    1.46 +    
    1.47 +    /* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
    1.48 +    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    1.49 +        orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
    1.50 +    }
    1.51 +    return orientationMask;
    1.52 +}
    1.53 +
    1.54 +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
    1.55 +{
    1.56 +    NSUInteger orientationMask = [self supportedInterfaceOrientations];
    1.57 +    return (orientationMask & (1 << orient));
    1.58 +}
    1.59 +
    1.60 +@end
    1.61 +
    1.62 +
    1.63  @interface SDL_splashviewcontroller : UIViewController {
    1.64      UIImageView *splash;
    1.65      UIImage *splashPortrait;
    1.66 @@ -98,6 +154,13 @@
    1.67  
    1.68      CGSize size = [UIScreen mainScreen].bounds.size;
    1.69      float height = SDL_max(size.width, size.height);
    1.70 +    /* FIXME: Some where around iOS 7, UILaunchImages in the Info.plist was introduced which explicitly maps image names to devices and orientations.
    1.71 +     This gets rid of the hardcoded magic file names and allows more control for OS version, orientation, retina, and device.
    1.72 +     But this existing code needs to be modified to look in the Info.plist for each key and act appropriately for the correct iOS version.
    1.73 +     But iOS 8 superscedes this process and introduces the LaunchScreen NIB which uses autolayout to handle all orientations and devices.
    1.74 +     Since we now have a LaunchScreen solution, this may never get fixed, 
    1.75 +     but this note is here for anybody trying to debug their program on iOS 7 and doesn't understand why their Info.plist isn't working.
    1.76 +     */
    1.77      self->splashPortrait = [UIImage imageNamed:[NSString stringWithFormat:@"Default-%dh.png", (int)height]];
    1.78      if (!self->splashPortrait) {
    1.79          self->splashPortrait = [UIImage imageNamed:@"Default.png"];
    1.80 @@ -207,10 +270,26 @@
    1.81      /* Keep the launch image up until we set a video mode */
    1.82      launch_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    1.83  
    1.84 -    UIViewController *splashViewController = [[SDL_splashviewcontroller alloc] init];
    1.85 -    launch_window.rootViewController = splashViewController;
    1.86 -    [launch_window addSubview:splashViewController.view];
    1.87 -    [launch_window makeKeyAndVisible];
    1.88 +    /* iOS 8 introduces LaunchScreen NIBs which use autolayout to handle all devices and orientations with a single NIB instead of multiple launch images.
    1.89 +     This is also the only way to get the App Store badge "Optimized for iPhone 6 and iPhone 6 Plus".
    1.90 +     So if the application is running on iOS 8 or greater AND has specified a LaunchScreen in their Info.plist, we should use the LaunchScreen NIB.
    1.91 +     Otherwise, we should fallback to the legacy behavior of launch screen images.
    1.92 +     */
    1.93 +    NSString* launch_screen_name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
    1.94 +    if( ([[UIDevice currentDevice].systemVersion intValue] >= 8) && (nil != launch_screen_name) ) {
    1.95 +        // iOS 8.0 and above uses LaunchScreen.xib
    1.96 +        SDL_launchscreenviewcontroller* launch_screen_view_controller = [[SDL_launchscreenviewcontroller alloc] init];
    1.97 +        launch_window.rootViewController = launch_screen_view_controller;
    1.98 +        [launch_window addSubview:launch_screen_view_controller.view];
    1.99 +        [launch_window makeKeyAndVisible];
   1.100 +    } else {
   1.101 +        // Anything less than iOS 8.0
   1.102 +
   1.103 +        UIViewController *splashViewController = [[SDL_splashviewcontroller alloc] init];
   1.104 +        launch_window.rootViewController = splashViewController;
   1.105 +        [launch_window addSubview:splashViewController.view];
   1.106 +        [launch_window makeKeyAndVisible];
   1.107 +    }
   1.108  
   1.109      /* Set working directory to resource path */
   1.110      [[NSFileManager defaultManager] changeCurrentDirectoryPath: [[NSBundle mainBundle] resourcePath]];