我有一个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()
}
}
}
答案 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()
}
}