Kon*_*ann 16 declarative swift swiftui property-wrapper
为了实现应用程序代码的简洁外观,我为每个包含逻辑的视图创建了 ViewModel。
一个普通的 ViewModel 看起来有点像这样:
class SomeViewModel: ObservableObject {
@Published var state = 1
// Logic and calls of Business Logic goes here
}
Run Code Online (Sandbox Code Playgroud)
并像这样使用:
struct SomeView: View {
@ObservedObject var viewModel = SomeViewModel()
var body: some View {
// Code to read and write the State goes here
}
}
Run Code Online (Sandbox Code Playgroud)
当视图父级未更新时,这可以正常工作。如果父级的状态发生变化,这个视图会被重绘(在声明性框架中很正常)。但是ViewModel 也会被重新创建,并且之后不会保存状态。与其他框架(例如:Flutter)相比,这是不寻常的。
在我看来,ViewModel 应该保留,或者 State 应该保留。
如果我用一个@State属性替换 ViewModel并int直接使用(在本例中)它会保持持久化并且不会重新创建:
struct SomeView: View {
@State var state = 1
var body: some View {
// Code to read and write the State goes here
}
}
Run Code Online (Sandbox Code Playgroud)
这显然不适用于更复杂的状态。如果我为@State(如 ViewModel)设置一个类,越来越多的事情无法按预期工作。
@StatePropertywrapper @ObservedObject?我知道通常,在内部视图中创建 ViewModel 是不好的做法,但可以通过使用 NavigationLink 或 Sheet 来复制这种行为。
有时,当您想到一个非常复杂的 TableView 时,将状态保留在ParentViewModel 中并使用绑定是没有用的,其中单元格本身包含很多逻辑。
对于个别情况总是有一种解决方法,但我认为如果不重新创建 ViewModel 会更容易。
我知道有很多关于这个问题的问题,都在谈论非常具体的用例。在这里我想谈谈一般问题,而不是深入讨论自定义解决方案。
当有一个状态改变的 ParentView 时,比如来自数据库、API 或缓存的列表(想想一些简单的事情)。通过 aNavigationLink您可能会到达一个详细信息页面,您可以在其中修改数据。通过更改数据,反应性/声明性模式会告诉我们也更新 ListView,然后“重绘” NavigationLink,这将导致重新创建 ViewModel。
我知道我可以将 ViewModel 存储在 ParentView / ParentView 的 ViewModel 中,但这是 IMO 的错误做法。并且由于订阅被销毁和/或重新创建 - 可能会有一些副作用。
Kon*_*ann 12
最后,苹果提供了一个解决方案:@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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,在StateObject重绘父视图时保留它的值,同时ObservedObject正在重置。
小智 5
我同意你的看法,我认为这是 SwiftUI 的许多主要问题之一。这就是我发现自己在做的事情,尽管它很恶心。
struct MyView: View {
@State var viewModel = MyViewModel()
var body : some View {
MyViewImpl(viewModel: viewModel)
}
}
fileprivate MyViewImpl : View {
@ObservedObject var viewModel : MyViewModel
var body : some View {
...
}
}
Run Code Online (Sandbox Code Playgroud)
您可以就地构建视图模型或将其传入,它会为您提供一个视图,该视图将在整个重建过程中维护您的 ObservableObject。
| 归档时间: |
|
| 查看次数: |
3131 次 |
| 最近记录: |