A Transaction is the context of one state-processing update. SwiftUI creates one for every
state change; it carries the animation to apply and a flag that disables animations set by
child views. Reach for it when a child view owns its animation and you need to override it.
Override From the Call Site
A leaf view with its own .animation(.spring()) cannot be re-animated from outside through
the normal API. withTransaction wraps the mutation in a custom transaction that wins.
AnimatedView(scale: scale ? 0.5 : 1)
.onTapGesture {
var transaction = Transaction(animation: .linear)
transaction.disablesAnimations = true
withTransaction(transaction) {
scale.toggle()
}
}
disablesAnimations = true turns off every animation declared inside the hierarchy, and the
transaction's own animation runs instead.
Per-View Transaction Modifier
The transaction modifier edits the current transaction for one view. Its closure receives
the Transaction as inout, so you can swap the animation or kill it for that view only.
VStack {
AnimatedView(scale: scale ? 0.5 : 1)
.transaction { $0.animation = .spring() }
AnimatedView(scale: scale ? 0.5 : 1)
.transaction { $0.disablesAnimations = true }
}
.onTapGesture { scale.toggle() }
One state change, two behaviors: the first view springs, the second snaps with no animation.
Gestures and Bindings
Gesture updating closures receive the transaction as their third parameter, so a drag can
animate its own state updates.
DragGesture()
.updating($offset) { value, state, transaction in
state = value.translation
transaction.animation = .interactiveSpring()
}
A binding can carry its own transaction too. Every write through the derived binding then runs with that animation context.
private var animatedBinding: Binding<Bool> {
var transaction = Transaction(animation: .interactiveSpring())
transaction.disablesAnimations = true
return $scale.transaction(transaction)
}