How to Create a Splash Screen With SwiftUI

Learn to build a splash screen that uses animation and SwiftUI to go beyond the typical static launch screen and keeps users interested while the app loads. By Rony Rozen.

Leave a rating/review
Download materials
Save for later
Share
Update note: Rony Rozen updated this tutorial for SwiftUI, Xcode 11. Derek Selander wrote the original.

Oh, the wonderful splash screen — a chance for developers to go wild with fun animations as the app frantically pings API endpoints for the critical data it needs to function. The splash screen, as opposed to a static, animation-free launch screen, can play an important role in an app: Keeping users interested while they wait for the app to start.

This tutorial will guide you step by step from an app with no splash screen to one with a cool splash screen that will be the envy of others. So, what are you waiting for?

Note: This tutorial assumes you’re comfortable with SwiftUI animations, states and modifiers. Rather than introducing those concepts, this tutorial focuses on using them to replicate a cool animation. To learn more about SwiftUI, check out SwiftUI: Getting Started.

Getting Started

In this tutorial, you’ll enhance an app called Fuber. Fuber is an on-demand ride-sharing service that allows passengers to request Segway drivers to transport them to different locations in urban environments.

Fuber has grown rapidly and now serves Segway passengers in over 60 countries, but it faces opposition from numerous governments as well as Segway unions for its use of contract Segway drivers. :]

Splash screen

Use the Download Materials button at the top or bottom of the page to download the starter project for this tutorial. Then, open the starter project and take a look around.

As you can see in ContentView.swift, all the app currently does is show SplashScreen for two seconds, then fade it away to reveal a MapView.

Note: In a production app, the exit criteria for this loop could be a handshake success to an API endpoint, which provides the app with the data necessary to continue.

The splash screen lives in its own module: SplashScreen.swift. You can see that it has a Fuber-blue background with a “F ber” label that’s waiting for you to add the animated ‘U’.

Build and run the starter project.

You’ll see a not-very-exciting static splash screen that transitions into the map (Fuber’s main screen) after a few seconds.

You’ll spend the remainder of this tutorial transforming this boring static splash screen into a beautifully-animated screen that will make your users wish the main screen would never load. Take a look at what you’ll build:

Note: If you’re running the macOS Catalina beta, you can use live previews in lieu of building and running on the simulator.

Understanding the Composition of Views and Layers

The new and improved SplashScreen will consist of several subviews, all conveniently organized in a ZStack:

  • The grid background consisting of tiles of a smaller “Chimes” image, which comes with the starter project.
  • The “F ber” text, with room for the animated FuberU.
  • The FuberU, which represents the circular white background for the ‘U’.
  • A Rectangle representing the square in the middle of FuberU.
  • Another Rectangle representing the line that goes from the middle of FuberU to its outer edge.
  • A Spacer view to make sure that the ZStack size will cover the entire screen.

Combined, these views create the Fuber ‘U’ animation.

RiderIconView

The starter project provides the Text and Spacer views. You’ll add the rest of the views in the following sections.

Now that you know how you’ll compose these layers, you can start creating and animating the FuberU.

Animating the Circle

When working with animations, it’s best to focus on the animation you’re currently implementing. Open ContentView.swift and comment out the .onAppear closure. It should look like this:

.onAppear {
//DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
//  withAnimation() {
//    self.showSplash = false
//  }
//}
}

This way, you won’t be distracted by the splash screen fading out to reveal the MapView after X seconds. Don’t worry, you’ll uncomment that part when you’re done and ready to ship.

You can now focus on the animation. Start by opening SplashScreen.swift and, right below SplashScreen‘s closing bracket, add a new struct called FuberU:

struct FuberU: Shape {
  var percent: Double
  
  // 1
  func path(in rect: CGRect) -> Path {
    let end = percent * 360
    var p = Path()

    // 2
    p.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width/2),
             radius: rect.size.width/2,
             startAngle: Angle(degrees: 0),
             endAngle: Angle(degrees: end),
             clockwise: false)
    
    return p
  }  
  // 3
  var animatableData: Double {
    get { return percent }
    set { percent = newValue }
  }
}

Here’s what you’re doing with this code:

  1. Implementing path(in:) as required by the Shape protocol.
  2. Using a path to draw an arc starting at 0 and ending at 360, i.e a full circle.
  3. Adding an extra property, so SwiftUI knows how to animate your shape.

In order to see your new type in action, you’ll set up some variables and an animation, then declare it on body with some modifiers.

Start by adding these variables right before the body element in the SplashScreen struct:

@State var percent = 0.0
let uLineWidth: CGFloat = 5

You’ll use these variables when initiating and modifying FuberU.

Then, add the following code after SplashScreen‘s struct closing bracket:

extension SplashScreen {
  var uAnimationDuration: Double { return 1.0 }
    
  func handleAnimations() {
    runAnimationPart1()
  }

  func runAnimationPart1() {
    withAnimation(.easeIn(duration: uAnimationDuration)) {
      percent = 1
    }
  }
}

handleAnimations() will be the basis for all of the different parts of the splash screen’s complex animation. It’s based on “magic numbers”, which you can play around with and tweak to match your exact taste later.

Finally, add the following code inside body, between the existing Text and Spacer elements.

FuberU(percent: percent)
 .stroke(Color.white, lineWidth: uLineWidth)
 .onAppear() {
   self.handleAnimations()
 }
 .frame(width: 45, height: 45, alignment: .center)

Here, you add the new circle, which will eventually represent a part of the Fuber ‘U’, to the stack at a specific position. In addition, you call handleAnimations() when the view appears.

Build and run your app:

You can see that something is happening, but it’s not exactly what you might expect. Your code is indeed drawing a circle, but only one time, and the circle’s border is way too thin. You want it to fill the entire circle. You’ll fix those problems right away.