如果未立即完成操作,则绑定到@Published var时,SwiftUI视图将不进行动画处理

时间:2020-03-10 11:23:11

标签: swiftui

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

下面是一个示例,其中视图模型中的1.0s计时器在更改@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()
            }

        }
    }

3 个答案:

答案 0 :(得分:4)

在视图模型工作流程中,您的withAnimation不执行任何操作,因为在这种情况下状态没有改变(这只是一个函数调用),因此仅计划了计时器,因此您宁愿使用它作为< / p>

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

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

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

通过Xcode 11.2 / iOS 13.2测试

答案 1 :(得分:2)

父视图为子视图的隐藏和显示设置动画。如果在第一个VStack的末尾放置一个.animation(.easeIn)(或.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)
  }
}

答案 2 :(得分:-1)

您必须对动画使用延迟,而不是使用计时器来延迟动画。

Button(action: {
    withAnimation(Animation.linear.delay(1.0)) {
        self.viewModel.showCircle.toggle()
    }
}) {
    Text("With ViewModel Observation")
}

否则,当您不使用计时器时,视图模型中的更改将按预期方式运行。

class ViewModel: ObservableObject {
    @Published var showCircle = true

    public func toggle() {

        /* other operations */

        self.showCircle.toggle()
    }

}