Last Updated: December 26, 2018
·
11.07K
· kerrishotts

Fade an iOS Splash Screen

I recently had the need to take the splash screen and crossfade to the app's content, mainly to prevent rather big (and annoying) change when dealing with dark themes. My splash screens don't know what theme you're on, and so if you're on a dark theme and my splash screen is white, there's going to be a ridiculously shocking transition from the white splash screen to the black UI.

Instead of that, I wanted something that would fade to the UI so as to prevent as shocking a transition. I found my answer in a couple of sources and then meddled with it to get what I needed. Take a look at https://gist.github.com/1026439 and https://gist.github.com/3798781.

One thing to note: my assets all take the status bar into account. That means that my iPhone Portrait splash screen is 20 points smaller than Xcode would really like it, and so I've got little exclamation points everywhere. I'm sure you could position the image appropriately if you wanted to keep the status bar area in your splash, but it's just something to be aware of.

UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.window.frame];

if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
    switch ( [[UIApplication sharedApplication] statusBarOrientation] )
    {
    case UIInterfaceOrientationLandscapeLeft:
    case UIInterfaceOrientationLandscapeRight:
            if ( [UIScreen mainScreen].scale == 2 ) { imageView.image = [UIImage imageNamed:@"Default-Landscape@2x~ipad.png"]; }
                                               else { imageView.image = [UIImage imageNamed:@"Default-Landscape~ipad.png"]; }
            [imageView setFrame: CGRectMake(0, 0, 1024, 748) ];
        break;
    case UIInterfaceOrientationPortrait:
    case UIInterfaceOrientationPortraitUpsideDown:
            if ( [UIScreen mainScreen].scale == 2 ) { imageView.image = [UIImage imageNamed:@"Default-Portrait@2x~ipad.png"]; }
                                               else { imageView.image = [UIImage imageNamed:@"Default-Portrait~ipad.png"]; }
        [imageView setFrame: CGRectMake(0, 0, 768, 1004) ];
        break;
    }
}
else
{
    // we're an iPhone or iPod touch. No rotation for you.
    if ( [UIScreen mainScreen].scale == 2 )
    {
        // are we a 3.5in? or a 4?
        if ([UIScreen mainScreen].bounds.size.height == 568.0f)
        {
            // 4 inch iPhone 5
            imageView.image = [UIImage imageNamed:@"Default-568h@2x.png"];                
            [imageView setFrame: CGRectMake(0, 0, 320, 548) ];
         }
         else
         {
            imageView.image = [UIImage imageNamed:@"Default@2x.png"];
            [imageView setFrame: CGRectMake(0, 0, 320, 460) ];
        }
     }
    else
    {
        imageView.image = [UIImage imageNamed:@"Default.png"];
        [imageView setFrame: CGRectMake(0, 0, 320, 460) ];
    }
}
[self.window.rootViewController.view addSubview:imageView];
[self.window.rootViewController.view bringSubviewToFront:imageView];

PKWaitDelay(2,{
[UIView transitionWithView:self.window
                  duration:1.00f
                   options:UIViewAnimationCurveEaseInOut
                 animations:^(void){
                     imageView.alpha = 0.0f;
                 }
                completion:^(BOOL finished){
                     [imageView removeFromSuperview];
                 }];}
       );
[self.window makeKeyAndVisible];

Seeing as the code above doesn't fit terribly well in the column, I've put a gist up at https://gist.github.com/4410510.

You may have a couple of questions -- first, where does this go? It should go in your AppDelegate.m file, just before the end of your didFinishLaunchingWithOptions method.

The next question you might have is why am I adding the image to my window's rootViewController? Precisely because rootViewController was appearing above the image in the view hierarchy, which meant all the animation was occurring behind the rootViewController and out of sight.

Finally, what's that PKWaitDelay() function? Well -- it's a macro for one, defined like this:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,dly*100000),dispatch_get_current_queue(), ^{ block })

So the transition code above is actually waiting about two seconds before we perform the fade -- why? My rootViewController and its subviews require a second or more to compose themselves, and if we kicked off the animation immediately, we get a very jarring transition while the device is busy composing the views, and then we get what little fade happens to be left. So I decided to force a small wait before the transition. Is it possible there's a device out there that has a poor animation? I suppose, but it really boils down to what works for you. Regardless, I don't see going any higher than 3 or 4s -- Apple expressly indicates that the app shouldn't in any way slow down the startup process intentionally. My 2s delay is not intended to slow anything down -- just to cover up the work going under the sheets, so-to-speak. But if I went to 4s, my iPad would be idling for 2s or more. Not good. I'll live with a jerkier transition on a slower device rather than slow down the current generations just to make sure an older generation doesn't see any visual hiccups.

Anyway, since fading the splash screen isn't terribly uncommon and can be very useful (especially for lessening the impact of a sudden transition), I thought I'd share it with you.

Oh -- one more thing. Is it perfect? Goodness no. For example, if I rotate the device in the middle of the transition, you get the image at the wrong size. I'm sure there's a way around it, but I haven't taken the time to figure that out yet. How about I leave as an exercise to the reader?