TL;DR: Implicit animations in SwiftUI use modifiers like
.animation
, automatically applying effects to state changes. Explicit animations, usingwithAnimation
orwithTransaction
, 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.
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.
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
.
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 whenisActive
changes..animation(animation:, body:)
Declares animations for specific elements without affecting the entire branch.