Getting Started with SwiftUI Animations

In this tutorial, you’ll learn how to add fancy animations with SwiftUI. You’ll go from basic animations to complex and custom spring animations. By Michael Katz.

Leave a rating/review
Download materials
Save for later
Share

SwiftUI brings a ton of new features and an easier way of writing UI code to all of Apple’s platforms. As an added bonus, it also has a new way of animating state transitions.

If you’re used to UIView animations, you may find these easier to write and understand. If you’re jumping in with SwiftUI, congratulations! This will let you “easeIn” to it. That’s an animation joke! :]

In this tutorial, you’ll learn the basics of SwiftUI animation, including:

  • The animation modifier.
  • withAnimation, the function which lets you animate state changes.
  • Custom animations.

It’s best to use Xcode 11.2.1 or later, which contains fixes for known animation bugs in the SwiftUI code. You’ll have an easier time if you’re running macOS Catalina, because Xcode will show the code and live preview side-by-side in the Canvas pane.

However, if you still have Mojave, that’s fine too; you can just build and run to see how code changes affect the app. In fact, this technique is still handy on Catalina, as the preview window can be buggy.

This tutorial assumes a working knowledge of SwiftUI. You should be comfortable with components such as Text, Image, HStack and VStack. If you’re just getting started with SwiftUI, check out this tutorial first.

Getting Started

Use the Download Materials button at the top or bottom of this tutorial to download the projects for this tutorial.

The tutorial app, MySolarSystem, is a simple inventory of the planets in your solar system (assuming you’re reading this on Earth). Once completed, tapping a row will show each planet with a list of that planet’s largest moons. From there, tapping a moon will provide additional information about the planet to the user.

Planet list with the moon view open

ContentView represents the main screen of the app. It is a List that makes a table from the array of Planets. Each element in the list is an HStack with several controls: an Image for the planet’s picture, a Text for the planet’s name, a Spacer and finally a Button, if the planet has any moons. The purpose of this button is to show information about the moons.

Diagram of the Views making up ContentView

Later in this tutorial, you’ll put the optional MoonList in the VStack. Tapping the moon button will show or hide it. This will be the first thing you’ll add and animate.

Preview Window

If you’re running on macOS Catalina, you have access to the Canvas in the editor pane. If you don’t see it, enable it by selecting Editor ▸ Canvas. This will show the view and live update as you change the code.

Device showing the live preview

If you click the Live Preview button, aka the play icon, it will make the preview interactive. Once you add animations and interactivity, you’ll be able to click buttons and observe state changes.

Note: As of this writing, the preview window is not 100% reliable. If the preview stops updating, check the top of the Canvas for the message: Automatic preview updating paused. If you see that, just click Resume to rebuild the preview. Also, if an animation doesn’t appear to be working, double check by running the app in the simulator or on a device. The live app is the source of truth for any UI behaviors.

Basic Animations

To add animations, the first thing you need is something to animate. So to start, you’ll create a state change that triggers an update to the UI.

Open ContentView.swift and add the following to the end of the VStack in makePlanetRow(planet:):

if self.toggleMoons(planet.name) {
  MoonList(planet: planet)
}

This checks toggleMoons(_:) to determine if that planet’s row should be toggled. If the moons are toggled on, then a MoonList view will appear in the VStack. This method is tied to the state property, showMoon.

Next, complete the action by setting the showMoon property. In the button’s action callback, add the following code:

self.showMoon = self.toggleMoons(planet.name) ? nil : planet.name

This code either clears showMoon if the user already pressed the button or sets it to the new planet if the user selects a different row.

Build and run the app. Tapping a row with a “moon disclosure” icon will show a cartoon map and a list of some that planet’s largest moons. The button also doubles in size to call attention to it. This is so the user knows where to tap to close the row.

Moon disclosure button activated

This flashes the moon list view in and out, which is hardly a great user experience. Fortunately, adding a little animation is easy.

Start by creating a smooth animation for the button size. After the scaleEffect modifier on the moon Image, add the following modifier:

.animation(.default)

This adds a default animation to the scale effect. If you expand or collapse a row, the button will now grow or shrink smoothly. If you notice some issues in the instantaneous appearance and disappearance of the moon view, don’t worry. You’ll fix that in the Animating State Changes section below.

Animation Timing

The animation(_:) modifier takes an Animation parameter.

There are several options that you can use for an animation. The basic ones are simple timing curves that describe how the speed of animation changes over the duration. These all modify the changing attributes, interpolating smoothly between the start and end values.

You have the following options:

  • .linear: This transitions the attribute from the start to end value evenly over time. This is a good timing curve for repeating animations, but it doesn’t look as natural as the eased functions. linear animation graph
  • .easeIn: An eased-in animation starts off slow and picks up speed over time. This is good for animations that start from a resting point and finish off-screen. easeIn animation graph
  • .easeOut: Eased-out animations start fast and end slow. This is good for animating something coming to a steady state or final position. easeOut animation graph
  • .easeInOut: Ease in and out curves combine both easeIn and easeOut. This is good for animations that start in one steady spot and end at another equilibrium. This is best for most applications. That is why this is timing curve used by .default. easeInOut animation graph
  • .timingCurve: This allows you to specify a custom timing curve. This is rarely needed and is out of the scope of this tutorial.

Most of the time, .default will be good enough for your needs. If you need something more, one of the basic timing functions will give you a little extra refinement. Give them a try by replacing .default with one of these to find out what’s best for you.

The graphs above show velocity over time for each of the timing curves. But how will the animations actually display? Here’s how each of the curves looks over time when you apply it to the scaleEffect of the moon button.

Linear

Linear changes to the moon button's scale

Linear

EaseIn

easeIn changes to the moon button's scale

EaseIn

EaseOut

easeOut changes to the moon button's scale

EaseOut

EaseInOut

easeInOut changes to the moon button's scale

EaseInOut