iOS Splash Screens Done Right

| Comments

I often get asked how to show something like a branding splash screen when an iOS app launches. While I think this is, in general, a bad practice, there are occasions when showing a quick intro screen can be appropriate. Or maybe it’s not a splash screen at all but something more like a user login screen. Especially to those new to the iOS platform, these kinds of tasks can be problematic.

Working with One-Offs

“I somehow got my app working just like I wanted, but now I don’t know how to put this splash screen at the beginning,” one might say. I see a lot of incantations and black magic being evoked on the app delegate’s window to try and get a splash screen to show up. This is never good, so let’s look at how this is achieved (easily) without fighting against Cocoa…

First, for one-offs like a splash screen, it’s important to basically architect your app’s navigation flow as if the one-offs didn’t exist. That is, construct your view controllers and such without even considering the one-offs. But once you have your app architecture in place, how do you interject it with these one-offs? Apple’s answer to this question is the modal view controller. Modal view controllers are perfect for branching out of your app’s regular navigation flow temporarily, returning to the main adventure (regular app flow) once the side quest (one-off) is over.

(I should clarify what I mean by modal view controller. There’s actually no such thing as a modal view controller. Rather, the idea is that you can present a view controller modally rather than, say, pushing it on to a navigation controller. However, classifying a view controller as modal is just convenient, so I’ll continue to do so.)

A little bit of confusion arises over splash screens in particular, because they need to basically be interjected at the very beginning of the app’s launch, before your first view controller becomes visible. One thought is to just insert some logic into your main view controller that manages the presentation and dismissal of your splash screen. While this can work, there are at least two problems with this design:

  1. It couples a particular view controller to your splash screen view controller, reducing flexibility
  2. It possibly requires a particular view controller (other than the splash screen view controller) to maintain state so it knows when it should or shouldn’t present your splash screen view controller

Proper Delegation of Responsibility

Instead, a better approach might be to let the app delegate trigger the presentation of your splash screen view controller:

Presenting a Splash Screen View Controller in the App Delegate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Your App Delegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    LTMasterViewController *masterViewController = [[LTMasterViewController alloc] initWithNibName:nil bundle:nil];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];

    self.window.rootViewController = navigationController;

    LTSplashScreenViewController *splashScreenViewController = [[LTSplashScreenViewController alloc] initWithNibName:nil bundle:nil];
    splashScreenViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

    [self.window.rootViewController presentModalViewController:splashScreenViewController animated:NO];

    return YES;
}

Note that I said “trigger the presentation,” because the app delegate itself cannot present a modal view controller. Only other view controllers can do this. However, at this (very early) point in the app’s lifecycle, we have a handle on the window’s root view controller. So there’s no reason why we can’t use this as the point to present our splash screen. (Note that we don’t animate the presentation because we want the splash screen to be visible immediately.)

In the above example, I set the view controller’s modalTransitionStyle property. “Why?” Good question. In this simple case, while I don’t want there to be any visual transition when I present the modal view controller, I do want it to have a transition when it’s dismissed. If your splash screen’s animations are more complex, then you probably don’t want to use the baked-in transitions and instead mess with CATransition or something of that nature.

From here it’s pretty trivial to let your splash screen do its job and dismiss itself afterward:

Presenting a Splash Screen View Controller for 3 Seconds
1
2
3
4
5
6
7
8
// Your Splash Screen View Controller

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [self performSelector:@selector(dismissModalViewControllerAnimated:) withObject:[NSNumber numberWithBool:YES] afterDelay:3.0];
}

This is a pretty trivial example, but it should be clear where view controllers should be managed. In a more complicated situation where your splash screen needs to do some animations or something, you’d just dismiss it when those animations finish. Otherwise, it’s fine to just tell the splash screen view controller to dismiss itself after a certain time elapses.

And that’s that. Your app delegate is the one that decides when to display the splash screen view controller, and the splash screen view controller decides when to dismiss itself. Given this, I imagine a common objection is, “If the app delegate decided when to show the splash screen, shouldn’t it also decide when to dismiss the splash screen?” I don’t necessarily disagree with this at a conceptual level, but requiring the app delegate to dismiss the splash screen view controller adds unnecessary bookkeeping. The splash screen view controller knows when it’s finished and can ask its parent view controller (indirectly) to dismiss it.

Wrapping Up

Understanding how and when to use the classes and techniques Apple makes available in the Cocoa platform is really the first step to solving any problem, regardless of the complexity. For simple problems such as presenting a splash screen, you can avoid a lot of pain by not fighting against the frameworks. In summary, here’s how a typical splash screen might be displayed:

  1. Keep the app architecture focused on the main app flow, not the splash screen
  2. Don’t couple any view controllers not directly related to the splash screen
  3. Let the app delegate inject the presentation behavior for the splash screen
  4. When the splash screen is finished, let it dismiss itself so the main app flow can begin

I created a little sample project that demonstrates the process discussed in this post: SplashScreen.zip. Just remember: Apple doesn’t recommend using the app launch as a primary branding opportunity.

Comments