SwiftUI view not animating when bound to @Published var if action isn't completed immediately
In the case of view model workflow your withAnimation
does nothing, because not state is changed during this case (it is just a function call), only timer is scheduled, so you'd rather need it as
Button(action: {
self.viewModel.toggle() // removed from here
}) {
Text("With ViewModel Observation")
}
...
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in
withAnimation { // << added here
self?.showCircle.toggle()
}
}
However I would rather recommend to rethink view design... like
VStack {
if showCircle2 { // same declaration as showCircle
Circle().frame(width: 100, height: 100)
}
Button(action: {
self.viewModel.toggle()
}) {
Text("With ViewModel Observation")
}
.onReceive(viewModel.$showCircle) { value in
withAnimation {
self.showCircle2 = value
}
}
}
Tested with Xcode 11.2 / iOS 13.2
The parent view animates the hiding and the showing of its child views. If you place an .animation(.easeIn)
(or .easeOut or whatever you like) at the end of your first VStack it should work as expected.
As such...
struct ContentView: View {
@State var showCircle = true
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
VStack {
if showCircle {
Circle().frame(width: 100, height: 100)
}
Button(action: {
withAnimation {
self.showCircle.toggle()
}
}) {
Text("With State Variable")
}
}
VStack {
if viewModel.showCircle {
Circle().frame(width: 100, height: 100)
}
Button(action: {
withAnimation {
self.viewModel.toggle()
}
}) {
Text("With ViewModel Observation")
}
}
}.animation(.easeIn)
}
}