How to override animations with transactions
How to override animations with transactions 관련
Updated for Xcode 15
SwiftUI provides a withTransaction()
function that allows us to override animations at runtime, for example to remove an implicit animation and replace it with something custom.
For example, this code toggles some text between small and large sizes, animating all the way because it has an implicit animation attached:
struct ContentView: View {
@State private var isZoomed = false
var body: some View {
VStack {
Button("Toggle Zoom") {
isZoomed.toggle()
}
Spacer()
.frame(height: 100)
Text("Zoom Text")
.font(.title)
.scaleEffect(isZoomed ? 3 : 1)
.animation(.easeInOut(duration: 2), value: isZoomed)
}
}
}
Transactions allow us to override existing animations on a case by case basis. For example, you might decide that in one particular circumstance you want the text’s animation to happen in a fast, linear way rather than it’s existing animation.
To do that, first create a new Transaction
instance using whatever animation you want, then set its disablesAnimations
value to true so you override any existing animations that would apply. When you’re ready, call withTransaction()
using your transaction object, then go ahead and adjust all the state you want to change – it will all be animated using your transaction.
To demonstrate this in action, here’s our same text scaling example code except using a transaction to insert a custom animation that overrides the implicit one:
struct ContentView: View {
@State private var isZoomed = false
var body: some View {
VStack {
Button("Toggle Zoom") {
var transaction = Transaction(animation: .linear)
transaction.disablesAnimations = true
withTransaction(transaction) {
isZoomed.toggle()
}
}
Spacer()
.frame(height: 100)
Text("Zoom Text")
.font(.title)
.scaleEffect(isZoomed ? 3 : 1)
.animation(.easeInOut(duration: 2), value: isZoomed)
}
}
}
For even more control you can attach a transaction()
modifier to any view you want, allowing you to override any transactions that apply to the view.
For example, we could add a second zooming text view to our example, still using a transaction to trigger the zoom animation, but this time we’re going to use the transaction()
modifier on the second text view so we disable any transactions on that one view – we’re overriding the override, in effect:
struct ContentView: View {
@State private var isZoomed = false
var body: some View {
VStack {
Button("Toggle Zoom") {
var transaction = Transaction(animation: .linear)
transaction.disablesAnimations = true
withTransaction(transaction) {
isZoomed.toggle()
}
}
Spacer()
.frame(height: 100)
Text("Zoom Text 1")
.font(.title)
.scaleEffect(isZoomed ? 3 : 1)
Spacer()
.frame(height: 100)
Text("Zoom Text 2")
.font(.title)
.scaleEffect(isZoomed ? 3 : 1)
.transaction { t in
t.animation = .none
}
}
}
}