问题
为了实现应用程序代码的简洁外观,我为每个包含逻辑的 View 创建了 ViewModel。
一个普通的 ViewModel 看起来有点像这样:
class SomeViewModel: ObservableObject {
@Published var state = 1
// Logic and calls of Business Logic goes here
}
并像这样使用:
struct SomeView: View {
@ObservedObject var viewModel = SomeViewModel()
var body: some View {
// Code to read and write the State goes here
}
}
当 Views Parent 没有被更新时,这可以正常工作。如果父级的状态发生变化,这个 View 会被重绘(在声明性框架中很正常)。 但是 ViewModel 也被重新创建,之后不再保存状态。与其他框架(例如:Flutter)相比,这是不寻常的。
在我看来,ViewModel 应该保留,或者 State 应该保留。
如果我用
@State
替换 ViewModel属性(property)和使用int
(在本例中)直接保持不变,不会重新创建 :struct SomeView: View {
@State var state = 1
var body: some View {
// Code to read and write the State goes here
}
}
这显然不适用于更复杂的国家。如果我为
@State
设置一个类(如 ViewModel)越来越多的事情没有按预期工作。问题
@State
@ObservedObject
的属性包装器? 我知道通常在内部 View 中创建 ViewModel 是不好的做法,但是可以通过使用 NavigationLink 或 Sheet 来复制这种行为。
有时,当您想到一个非常复杂的 TableView 时,将 State 保留在 ParentsViewModel 中并使用绑定(bind)是没有用的,其中 Cell 本身包含很多逻辑。
对于个别情况,总有一种解决方法,但我认为如果不重新创建 ViewModel 会更容易。
重复的问题
我知道有很多关于这个问题的问题,都在谈论非常具体的用例。在这里我想谈谈一般问题,而不是太深入地讨论自定义解决方案。
编辑(添加更详细的示例)
当拥有一个改变状态的 ParentView 时,比如来自数据库、API 或缓存的列表(想想一些简单的事情)。通过
NavigationLink
您可能会到达一个详细信息页面,您可以在其中修改数据。通过更改数据,响应式(Reactive)/声明式模式会告诉我们也更新 ListView,然后它会“重绘”NavigationLink
。 ,这将导致 ViewModel 的重新创建。我知道我可以将 ViewModel 存储在 ParentView/ParentView 的 ViewModel 中,但这是 IMO 的错误做法。而且由于订阅被销毁和/或重新创建 - 可能会有一些副作用。
最佳答案
最后,苹果提供了一个解决方案:@StateObject
.
通过替换 @ObservedObject
与 @StateObject
我最初的帖子中提到的一切都有效。
不幸的是,这仅在 ios 14+ 中可用。
这是我的 Xcode 12 Beta 代码(2020 年 6 月 23 日发布)
struct ContentView: View {
@State var title = 0
var body: some View {
NavigationView {
VStack {
Button("Test") {
self.title = Int.random(in: 0...1000)
}
TestView1()
TestView2()
}
.navigationTitle("\(self.title)")
}
}
}
struct TestView1: View {
@ObservedObject var model = ViewModel()
var body: some View {
VStack {
Button("Test1: \(self.model.title)") {
self.model.title += 1
}
}
}
}
class ViewModel: ObservableObject {
@Published var title = 0
}
struct TestView2: View {
@StateObject var model = ViewModel()
var body: some View {
VStack {
Button("StateObject: \(self.model.title)") {
self.model.title += 1
}
}
}
}
如您所见,StateObject
在重绘父 View 时保持它的值(value),而 ObservedObject
正在重置。
关于SwiftUI:ObservableObject 在重绘时不会保持其状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61676823/