I have an interesting situation in regards to animations in SwiftUI. In its simplified form, I have a view that shows a rectangle which, when tapped, should toggle a Bool
binding and represent the change with an animated transition of color. But it seems like the animation doesn't happen when the view is inside a NavigationView
and the binding is coming from a StateObject
instead of simple local state. I can't explain why that would be the case, and would appreciate any thoughts.
Below is the code that shows a simplified case that reproduces the issue. The app code isn't particularly interesting; it's the default code that creates a WindowGroup
and shows an instance of ContentView
in it.
import SwiftUI
class AppState: ObservableObject {
@Published var isRed = false
}
struct RectView: View {
@Binding var isRed: Bool
var body: some View {
Rectangle()
.fill(isRed ? Color.red : Color.gray)
.frame(width: 75, height: 75, alignment: .center)
.onTapGesture {
withAnimation(.easeInOut(duration: 1)) {
isRed.toggle()
}
}
}
}
struct ContentView: View {
@StateObject var appState = AppState()
@State private var childViewIsRed = false
var body: some View {
VStack {
NavigationView {
List {
NavigationLink("Link with binding to state object", destination: RectView(isRed: $appState.isRed))
NavigationLink("Link with binding to state variable", destination: RectView(isRed: $childViewIsRed))
}
}
.frame(height: 300)
RectView(isRed: $appState.isRed)
RectView(isRed: $childViewIsRed)
}
}
}
The gif/video below is me demonstrating four things, tapping on these views from bottom to top:
- First I tap on the very bottom rectangle - the one with a binding to a
@State
property. It toggles with animation as expected. I tap again to leave it gray. - Then I tap the second rectangle from the bottom - one with a binding to a
@Published
property in the@StateObject
. All is well. - Next, I tap on the
NavigationLink
that leads to a rectangle that is bound to the local@State
property. When I tap on the rectangle the transition is animated fine. The very bottom rectangle also animates, which makes sense since they are bound to the same property. - Finally I tap on the top
NavigationLink
, which leads to a rectangle bound to the@Published
property in the@StateObject
. When I tap on this rectangle though, there is no animation. The rectangle snaps to red. The rectangle below it (which is bound to the same property) animates fine, proving that the property is indeed toggled. But there is no animation inside theNavigationView
. Why? What am I missing?
I've searched for existing questions. I'm aware that there are some around NavigationView
(
CodePudding user response:
To be honest with you, I am not sure why, but utilizing the animation modifier on the RectView
allows the animation to occur without any issues.
struct RectView: View {
@Binding var isRed: Bool
var body: some View {
Rectangle()
.fill(isRed ? Color.red : Color.gray)
.frame(width: 75, height: 75, alignment: .center)
.animation(.easeOut(duration: 1), value: isRed)
.onTapGesture { isRed.toggle() }
}
}