I have a working animation in a SwiftUI view, which breaks, when I put that view into a TabView
, go to the other tab and come back. How can I fix it?
My view is more complex, but I boiled it down to the following:
import SwiftUI
@main
struct AnimationTestApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Text("Tab 1")
}
Text("Content 2")
.tabItem {
Text("Tab 2")
}
}
}
}
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
@Namespace var namespace
var body: some View {
VStack {
Button(action: {
viewModel.move()
}) {
Text("Move")
}
ZStack() {
Rectangle()
.fill(.gray)
.frame(width: 300, height: 300, alignment: .center)
if viewModel.moved {
RectView()
.matchedGeometryEffect(id: 0, in: namespace, properties: .frame)
.animation(Animation.default.speed(0.5))
.offset(x: 0, y: -130)
} else {
RectView()
.matchedGeometryEffect(id: 0, in: namespace, properties: .frame)
.animation(Animation.default.speed(0.5))
.offset(x: 0, y: 130)
}
}
}
}
}
struct RectView: View {
var body: some View {
Rectangle()
.frame(width: 100, height: 30, alignment: .center)
}
}
final class ViewModel: ObservableObject {
@Published var moved = false
func move() {
moved = !moved
}
}
Here is an screen recording of that code. The RectView
animates smoothly up and down when tapping Move
. After switching to Tab 2
and going back, the first time I tap Move
, it jumps without animation. The next taps are animated again.
In reality the view model is more complex. The state (in this case: moved
) is changed some time after hitting the button. Some work is done on a background thread and then the state change is triggered on the main thread. This is why I can't move the animation into the Button
action.
Also the view is more complex. The RectView
is removed from deep in the view hierarchy and added somewhere entirely else.
The method func animation(_ animation: Animation?)
is deprecated in iOS 15. The problem is already existing in iOS 14 forever. I also tried removing the animation
modifiers and put the moved = !moved
into an withAnimation { }
block. Still the same result.
How could I fix that while keeping the TabView
?