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)
  }
}

Tags:

Swiftui