如果操作未立即完成,SwiftUI 视图在绑定到 @Published var 时不会动画

lep*_*olt 10 swiftui

我有一个 SwiftUI 视图,它根据状态换出某些控件。我正在尝试使用 MVVM,所以我的大部分/所有逻辑都被推到了视图模型中。我发现在执行修改@Published var视图模型上的a 的复杂操作时,View不会动画。

这是一个示例,其中视图模型中的 1.0 秒计时器模拟在更改@Published var值之前正在完成的其他工作:

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


    class ViewModel: ObservableObject {
        @Published var showCircle = true

        public func toggle() {
            // Do some amount of work here. The Time is just to simulate work being done that may not complete immediately.
            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in
                self?.showCircle.toggle()
            }

        }
    }
Run Code Online (Sandbox Code Playgroud)

Asp*_*eri 7

在视图模型工作流的情况下,你withAnimation什么都不做,因为在这种情况下状态没有改变(它只是一个函数调用),只安排了计时器,所以你宁愿需要它

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我宁愿建议重新考虑视图设计......就像

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用 Xcode 11.2 / iOS 13.2 测试


Dan*_*ary 5

父视图为其子视图的隐藏和显示设置动画。.animation(.easeIn)如果您在第一个 VStack 的末尾放置一个(或 .easeOut 或任何您喜欢的内容),它应该按预期工作。

像这样...

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)
  }
}
Run Code Online (Sandbox Code Playgroud)