我的视图是由存储在ViewModel
中的状态决定的。有时视图可能会调用其ViewModel
上的函数,导致异步状态更改。
如何在View
中设置状态更改的动画效果?
这里有一个人为的例子,调用viewModel.change()
将导致视图改变颜色。
- 预期行为:从蓝色到红色缓慢溶解。
- 实际行为:立即从蓝色变为红色。
class ViewModel: ObservableObject {
@Published var color: UIColor = .blue
func change() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.color = .red
}
}
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
Color(viewModel.color).onAppear {
withAnimation(.easeInOut(duration: 1.0)) {
self.viewModel.change()
}
}
}
}
如果我删除ViewModel
并将状态存储在视图本身中,一切都按预期工作。
struct ContentView: View {
@State var color: UIColor = .blue
var body: some View {
Color(color).onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
withAnimation(.easeInOut(duration: 1.0)) {
self.color = .red
}
}
}
}
}
3条答案
按热度按时间omqzjyyz1#
使用.animation()
您可以在主体内容或任何子视图上使用
.animation(...)
,但它将对视图的所有更改进行动画处理。让我们考虑一个例子,当我们通过ViewModel进行两次API调用并在body内容上使用
.animation(.default)
时:结果如下:
你可以看到我们在两个动作上都有动画。这可能是首选的行为,但在某些情况下,你可能希望单独控制每个动作。
使用.onReceive()
假设我们想要在第一次API调用(
fetchArticle
)之后动画视图,但是在第二次API调用(fetchContent
)时-只重绘视图而不动画。换句话说-在title
接收时动画视图,但在content
接收时不动画视图。为了实现这一点,我们需要:
1.在视图中创建一个单独的属性
@State var title = ""
。1.在整个视图中使用这个新属性,而不是
viewModel.title
。1.声明
.onReceive(viewModel.$title) { newTitle in ... }
。这个闭包将在发布者(viewModel.$title
)发送新值时执行。在这一步,我们可以控制View中的属性。在我们的例子中,我们将更新View的title
属性。1.在闭包中使用
withAnimation {...}
来动画化更改。因此,当
title
更新时,我们会有动画。当接收到ViewModel的新content
值时,我们的View只是更新而没有动画。结果如下:
vawmfj5a2#
看起来好像在异步闭包中使用
withAnimation
会导致颜色没有动画,而是立即改变。删除 Package
asyncAfter
,或者删除withAnimation
调用并在ContentView
的body
中添加animation
修饰符(如下所示)应该可以解决这个问题:在本地测试(iOS 13.3,Xcode 11.3),这也似乎是溶解/褪色从蓝色到红色,因为你的意图。
enxuqcxy3#
更新@Seb Jachec对Xcode 14.2的回答,以修复已弃用的警告: