Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 57

Implicit vs. Explicit Animations in SwiftUI

Recommended resources

Hello! Welcome to the first lesson of our new module, "SwiftUI Animation Essentials."

In the previous modules, you've built a strong foundation in creating dynamic, interactive user interfaces with UIKit, culminating in implementing custom transitions and haptic feedback. Now, we will shift our focus to SwiftUI, Apple's modern, declarative UI framework. As an experienced developer, you'll find that SwiftUI approaches animation from a fundamentally different perspective, moving from an imperative "how-to" model to a declarative "what-to-do" model.

Today's lesson addresses a foundational concept in this new paradigm. Our learning outcome is to differentiate between implicit and explicit animations in SwiftUI. Understanding this distinction is the first and most crucial step to mastering SwiftUI's powerful and intuitive animation system.

1. The Declarative Shift: From UIKit to SwiftUI

Before we dive into the specifics, let's set the stage. In UIKit, you write imperative code to animate views: you create a UIView.animate block and manually specify the start and end states within a closure.

SwiftUI is different. It's a state-driven framework. You declare what your UI should look like for any given state, and SwiftUI automatically handles updating the view when the state changes. Animations are simply a way to describe how SwiftUI should transition between those states.

To understand this, it helps to know that SwiftUI maintains two representations of your UI.

The first step to SwiftUI's Animation System

This article from Flyingharley.dev provides a concise overview of the core concepts behind SwiftUI's rendering and animation system, which will be a helpful bridge from your UIKit background.

Please read the introduction and the sections "SwiftUI vs UIKit Animation" and "Core Animation Basics" (up to, but not including, the "Implicit vs Explicit Animations" subsection). Focus on the distinction between the declarative vs. imperative approach and the concepts of the View Tree vs. the Render Tree.

This state-driven flow is the "why" behind SwiftUI's animation system. When a state variable (e.g., a @State property) changes, SwiftUI recalculates the parts of the view hierarchy that depend on it. If an animation is requested for that change, SwiftUI doesn't just jump to the new state; it interpolates the view's properties over time from the old values to the new ones.

The official WWDC talk below gives a fantastic visualization of this process.

WWDC23: Explore SwiftUI animation | Apple

This clip from an Apple WWDC talk explains the anatomy of a view update. This will give you a solid mental model for how SwiftUI handles changes.

Watch from 01:54 to 04:13. Pay close attention to the concepts of a 'transaction' and how SwiftUI updates the 'dependency graph' when a state changes.

With this foundational model in mind, let's explore the two primary ways to tell SwiftUI to perform these animated transitions.

SwiftUI Animation Types Flowchart
A simple flowchart showing the two main categories of animations in SwiftUI: implicit and explicit.

2. Explicit Animation: The withAnimation Block

Explicit animation is often the simplest way to get started. You use it when you want to animate all the consequences of a specific state change. You achieve this by wrapping the state change itself in a withAnimation closure.

The mental model is: "When I perform this action, animate whatever UI changes result from it."

This short video provides a clear, practical demonstration of withAnimation.

.animation() vs withAnimation(): what's the difference? (SwiftUI)

Vincent Pradeilles offers a very direct comparison in his video. We'll start by focusing on how withAnimation works.

Watch from 01:03 to 01:36. Notice how wrapping the state change in withAnimation causes both views that depend on that state to animate. This demonstrates its 'global' nature relative to the state change.

As you saw, withAnimation is imperative in its trigger but declarative in its effect. You explicitly trigger the animation, but you don't specify which views to animate—SwiftUI figures that out for you.

Here's a code example:

struct ExplicitAnimationExample: View {
    @State private var isScaled = false

    var body: some View {
        VStack(spacing: 20) {
            Circle()
                .frame(width: 80, height: 80)
                .scaleEffect(isScaled ? 1.5 : 1.0) // This depends on `isScaled`
            
            Rectangle()
                .frame(width: 100, height: 60)
                .opacity(isScaled ? 0.5 : 1.0) // This also depends on `isScaled`

            Button("Animate") {
                // All animatable changes caused by this state toggle will be animated.
                withAnimation {
                    isScaled.toggle()
                }
            }
        }
    }
}

When the button is tapped, the withAnimation block tells SwiftUI to open an animated "transaction." When isScaled changes within that block, SwiftUI sees that the scaleEffect of the Circle and the opacity of the Rectangle need to change. Because the transaction is animated, it smoothly interpolates both properties.


3. Implicit Animation: The .animation() Modifier

Implicit animation takes a different approach. Instead of wrapping the state change, you attach an .animation() modifier directly to a view.

The mental model here is: "I'm attaching a 'how-to-animate' rule to this view. If this view needs to change because a specific value changes, it should use this animation."

Let's return to the same video to see the contrast.

.animation() vs withAnimation(): what's the difference? (SwiftUI)

Now let's look at the other side of the coin: the .animation modifier.

Watch from 00:23 to 00:54. Observe how the .animation modifier is 'scoped' and only affects the view it's attached to, even though the state change affects both views.

A crucial best practice for implicit animations is to use the value parameter. This tells SwiftUI to only apply the animation when that specific value changes. This prevents unintended animations when other properties of the view change.

struct ImplicitAnimationExample: View {
    @State private var isShifted = false

    var body: some View {
        VStack(spacing: 20) {
            // This view has an animation rule attached to it.
            Circle()
                .frame(width: 80, height: 80)
                .offset(x: isShifted ? 100 : 0)
                // This animation will ONLY run when `isShifted` changes.
                .animation(.easeInOut, value: isShifted)
            
            // This view has NO animation rule. It will change instantly.
            Rectangle()
                .frame(width: 100, height: 60)
                .offset(x: isShifted ? -100 : 0)

            Button("Animate") {
                // No `withAnimation` here. The animation is handled by the view modifier.
                isShifted.toggle()
            }
        }
    }
}

In this example, when isShifted toggles, only the Circle animates because it's the only view with an .animation modifier monitoring that value. The Rectangle moves instantly.


4. Comparing and Combining

You now have two distinct tools for animation. The key is understanding their scope, priority, and when to use them.

This article provides an excellent, scannable summary that crystallizes the differences.

Mastering SwiftUI Animations: Implicit vs. Explicit Guide

This guide by Fatbobman offers a clear breakdown and a very useful summary table comparing implicit and explicit animations.

Read the entire article. Pay special attention to the "Core Differences Summary" table, as it's a perfect cheat sheet for remembering the distinction. Note the rule about priority: implicit animations override explicit ones.

The most important takeaway from that article is the priority rule: If a view is affected by an explicit withAnimation block but also has its own implicit .animation modifier, the implicit animation wins.

Consider this scenario:

// ...
Button("Animate") {
    withAnimation(.spring()) { // Explicit: Proposes a spring animation
        isToggled.toggle()
    }
}

// ... somewhere in the view hierarchy
Text("Hello")
    .opacity(isToggled ? 0 : 1)
    // Implicit: Overrides the spring with an ease-in-out curve
    .animation(.easeInOut, value: isToggled) 

In the code above, the Text view's opacity will fade using an easeInOut curve, not the spring animation proposed by the withAnimation block. The .animation modifier provides a more specific, localized instruction that takes precedence.


Conclusion

Congratulations on taking your first step into SwiftUI animations! Understanding the fundamental difference between the global, action-oriented withAnimation and the localized, view-oriented .animation modifier is the key to building predictable and powerful animations.

Key Takeaways:

  • Explicit Animation (withAnimation):
    • What it is: A function that wraps a state change.
    • Scope: Affects all animatable view properties that change as a result of the state update within the block.
    • When to use it: For broad changes where you want a consistent animation across multiple views affected by a single user action (e.g., toggling a mode, showing/hiding a sheet). It acts as a "default" animation for a transaction.
  • Implicit Animation (.animation(...)):
    • What it is: A view modifier.
    • Scope: Affects only the view it is attached to (and its child views, unless they have their own override). It's triggered when the monitored value changes.
    • When to use it: For fine-grained control over a specific view's animation, independent of others.
  • Priority Rule: When both are present, the implicit .animation modifier on a view overrides the explicit withAnimation block.

Preview of the Next Lesson:

Now that you know the two main ways to trigger an animation, our next lesson will focus on how to fine-tune their appearance. We will learn how to customize animation timing and curves using the .animation() and withAnimation modifiers, exploring concepts like spring physics and timing curves to bring your UI to life with more natural and engaging motion.