💡 SwiftUI: Implicit vs Explicit Animations

Get weekly handpicked updates on Swift and SwiftUI!

TL;DR: Implicit animations in SwiftUI use modifiers like .animation, automatically applying effects to state changes. Explicit animations, using withAnimation or withTransaction, offer centralized control and can override implicit behaviors. Combine both for tailored animation management.

Background

SwiftUI provides two animation mechanisms: implicit animations and explicit animations. Many developers find it challenging to understand their differences and application scenarios. This article compares the two through examples, analyzing their use cases and considerations.

Implicit Animations

Implicit animations are declared using modifiers like .animation or .transaction, specifying the animation effects applied during state changes. These animations propagate down the view hierarchy and can be overridden.

Swift
struct ImplicitAnimationDemo: View {
    @State private var isActive = false
    var body: some View {
        VStack {
            VStack {
                Text("Hello")
                    .font(.largeTitle)
                    .offset(x: isActive ? 200 : 0)
                    // Applies a .smooth animation when `isActive` changes
                    .animation(.smooth, value: isActive)

                Text("World")
                    .font(.largeTitle)
                    .offset(x: isActive ? 200 : 0)
            }
            // Applies a .linear animation with speed adjustment to VStack and its elements
            .animation(.linear.speed(3), value: isActive)

            // No animation applied
            Text("No Animation")
                .offset(x: isActive ? 200 : 0)

            Toggle("Active", isOn: $isActive)
                .padding()
        }
    }
}

When running the code, “No Animation” will move without animation due to the lack of an explicit declaration. “World” will use the .linear.speed(3) animation applied to the VStack, while “Hello” will use the .smooth animation.

Explicit Animations

Explicit animations are defined imperatively using withAnimation or withTransaction. They are suited for scenarios requiring centralized control over state change animations.

Swift
struct ExplicitAnimationDemo: View {
    @State private var isActive = false
    var body: some View {
        VStack {
            VStack {
                Text("Hello")
                    .font(.largeTitle)
                    .offset(x: isActive ? 200 : 0)
                    // Applies a .smooth animation when `isActive` changes
                    .animation(.smooth, value: isActive)

                Text("World")
                    .font(.largeTitle)
                    .offset(x: isActive ? 200 : 0)
            }
            // Applies a .linear animation with speed adjustment to VStack and its elements
            .animation(.linear.speed(0.5), value: isActive)

            // No implicit animation applied
            Text("No Animation")
                .offset(x: isActive ? 200 : 0)
            
            Button("Toggle") {
                withAnimation(.spring) {
                    isActive.toggle()
                }
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

In this example, “No Animation” will use the .spring animation defined in the withAnimation block, as it is affected by the isActive state change without an explicit implicit animation.

Comparison

  • Complementary Mechanisms: Implicit and explicit animations complement each other, catering to different animation needs.
  • Fine-tuning vs Central Control: Implicit animations allow precise control for specific elements, while explicit animations are better for centralized state change management.
  • Default Priority: Explicit animations provide default animation effects for views without implicit animations, ensuring consistency.
  • Animation Suppression: Explicit animations can suppress implicit animations using disablesAnimations.
Swift
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
    isActive.toggle()
}

Considerations for Implicit Animations

SwiftUI has introduced new implicit animation declarations over time. When possible, use the latest options for more precise animation control and guidance.

  • .animation(.spring) Applies .spring animation to all state changes within the current branch, regardless of specific states. Deprecated after iOS 14.
  • .animation(.spring, value: isActive) Applies .spring animation only when isActive changes.
  • .animation(animation:, body:) Declares animations for specific elements without affecting the entire branch.

Further Reading