Let's take a look at the following Code Snippet:
struct ContentView: View {
@State private var isViewHidden: Bool = false
let data = [1,2,3,4,5,6,7]
public var body: some View {
VStack {
Button("Hide", action: {
withAnimation {
isViewHidden.toggle()
}
})
ForEach(data, id: \.self) { _ in
VStack {
Text("Foo")
if isViewHidden {
Text("Bar").animation(nil)
}
}.padding().background(Color.green)
}
}
}
}
I would expect that the Text("Hide") will animate the position inside the parent VStack. But it will stick to its last position and fade from there and also animate back to that position. Is there a possibility to give this animation a more natural feel so it will animate inside its parent.
The problem is that
Bar
is a newView
and thus just fades or appears at its final destination, so its position doesn't animate because it previously wasn't on screen.The trick is to keep
Bar
on screen at all times and just adjust itsopacity
..offset
is used to keep the view from growing due to the wordBar
that isn't always present. A blank text viewText(" ")
is added or removed to cause the view to grow as before.Here's the workaround that gives a better looking animation:
Here it is running in the simulator:
Solution 2: Use
.matchedGeometryEffect
Here's a solution that uses
.matchedGeometryEffect
to animate betweenText("Bar").frame(height: 0)
andText("Bar")
.