I have a very simple view that only shows a Text
, a Shape
, and a Button
stacked vertically in a ScrollView
. The Shape
is a Capsule
and is conditionally shown only when showCapsule
is true
.
struct ContentView: View {
@State var showCapsule = true
var body: some View {
ScrollView {
VStack(spacing: 16) {
Text("Why, oh why? ")
.font(.headline)
if showCapsule {
Capsule()
.frame(maxWidth: .infinity)
.frame(height: 100)
.foregroundColor(.blue)
}
Button {
showCapsule.toggle()
} label: {
Text(showCapsule ? "Hide" : "Show")
}
.buttonStyle(.bordered)
}
.frame(maxWidth: .infinity)
.padding()
}
.animation(.default, value: showCapsule)
}
}
Observed and expected animation
I want to animate the appearance and disappearance of the Capsule
, but the result is totally not what I want. While the Capsule fades out (which is okay), the button is animated in two different ways simultaneously:
- Its background shape (the grey rounded rectangle) move from the old to its new position.
- Its text fades out at its old position and fades in at its new position.
Of course, (2) is not what I want. Instead, I want the button to move as a unit: The entire thing should move from its old to its new position while the text is faded inside of it.
The broader picture
This is a minimal example for a broader question: How do I animate changes to a view that is semantically the same but value-wise different?
In the Button
initializer, I use a ternary operator to conditionally pass a different string to its Text
label:
Button {
showCapsule.toggle()
} label: {
Text(showCapsule ? "Hide" : "Show")
}
Text("Hide")
is a different value than Text("Show")
, so I guess that's why SwiftUI can't identify them and doesn't "understand" that it should animate them in place. I observed the same behavior with custom views with let
constants. Is there a way to make SwiftUI treat such views – and especially this Button
– as a unit and animate them correctly?
Note
I'm not looking for a workaround like using two Text
fields (or Buttons
) and show/hide them by setting their opacity accordingly. Rather looking for a general solution for this kind of problem that solves the identity problem rather than patching its symptoms.