What is the difference between implicit and explicit animation, and how do transitions fit in?
Predict first
What does a fresh Transaction carry, and what after setting an animation?
import SwiftUI
var t = Transaction()
print(t.animation == nil)
t.animation = .easeIn
print(t.animation == .easeIn)
- false, then false
- true, then true
- true, then false
Mental model. An explicit animation is saying “do this move gracefully”. An implicit animation is telling one actor “whenever this cue changes, glide”.
An implicit animation is attached to a view: .animation(.spring, value: flag) animates changes to that value in everything above the modifier. Always pass a value:; the old form without one animated every change and is deprecated. An explicit animation wraps the state change: withAnimation(.easeInOut) { flag.toggle() } animates every view that depends on that state, wherever it is. Both create a Transaction that carries the animation (and flags such as disablesAnimations) down the update, and a view can override it for itself with .transaction or switch it off with .animation(nil, value:). Use explicit animation for user actions and implicit for a specific view’s reaction to a value.
What can animate is anything SwiftUI can interpolate: the animatable data of modifiers (opacity, offset, scale, rotation, colour, frame). Custom shapes and modifiers animate by exposing an animatableData. A change that is not interpolation, a view appearing or disappearing (a different if branch, a changed .id), uses a transition (.transition(.slide), .opacity, .asymmetric, custom). For two views that are “the same thing in two places” use matchedGeometryEffect with a shared id and namespace, or navigation zoom transitions. For multi-step or physics effects use keyframeAnimator. Prefer springs for natural motion, keep durations short, honour Reduce Motion (accessibilityReduceMotion), and never rely on animation to carry meaning.
A transaction starts with no animation, and withAnimation or .animation simply put one into it for the update.
Common traps
- Using
.animation(_:)withoutvalue:. It animated unrelated changes and is deprecated. - Expecting a view that is removed from the hierarchy to animate its value. Insertion and removal use transitions.
They might follow up with
- How do you animate a custom shape? Implement
animatableDataso SwiftUI can interpolate its parameters. - How do you animate a shared element between two screens?
matchedGeometryEffector a navigation zoom transition.