从目标视图更新SwiftUI列表数据会导致意外行为
我经常遇到这样的情况,从目标视图更新SwiftUI列表数据会导致意外行为,swiftui,combine,Swiftui,Combine,我经常遇到这样的情况,列表将@ObservedObject数据存储中的一些项目显示为导航链接 选择NavigationLink时,将显示详细视图。此视图有一个简单的切换连接到其视图模型类上的@已发布变量 当出现DetailView时(onAppear:),其视图模型设置已发布的var,该var控制切换为真,并触发一个异步请求,该请求将更新主数据存储,导致上一屏幕中的列表也更新 问题是,当发生这种情况时(从细节视图中触发的操作重新加载列表),DetailViewModel的多个实例似乎被保留,并且
列表
将@ObservedObject
数据存储中的一些项目显示为导航链接
选择NavigationLink
时,将显示详细视图。此视图有一个简单的切换
连接到其视图模型类上的@已发布
变量
当出现DetailView时(onAppear:
),其视图模型设置已发布的var
,该var控制切换
为真
,并触发一个异步请求,该请求将更新主数据存储,导致上一屏幕中的列表
也更新
问题是,当发生这种情况时(从细节视图中触发的操作重新加载列表
),DetailViewModel的多个实例似乎被保留,并且DetailView开始接收来自错误的发布者的事件
第一次到达详细信息屏幕时,行为正确(如下面的代码所示),切换设置为true
,并且存储更新,在导航回列表时,然后再次导航到另一个详细视图,在出现时切换设置为true
,但这次当重新加载存储的代码执行时,切换设置回false
我的理解是,当重新加载列表
并创建新的DetailView和ViewModel(作为导航链接
的目标)时,isOn
变量的初始值控制切换
(即false
)正在以某种方式触发对当前显示屏幕的切换的更新
我是不是遗漏了什么
import SwiftUI
import Combine
class Store: ObservableObject {
static var shared: Store = .init()
@Published var items = ["one", "two"]
private init() { }
}
struct ContentView: View {
@ObservedObject var store = Store.shared
var body: some View {
NavigationView {
List(store.items, id: \.self) { item in
NavigationLink(item, destination: ItemDetailView())
}
}
}
}
struct ItemDetailView: View {
@ObservedObject var viewModel = ItemDetailViewModel()
var body: some View {
VStack {
Toggle("A toggle", isOn: $viewModel.isOn)
Text(viewModel.title)
Spacer()
} .onAppear(perform: viewModel.onAppear)
}
}
class ItemDetailViewModel: ObservableObject {
@ObservedObject var store: Store = .shared
@Published var isOn = false
var title: String {
store.items[0]
}
func onAppear() {
isOn = true
asyncOperationThatUpdatesTheStoreData()
}
private func asyncOperationThatUpdatesTheStoreData() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
self?.store.items = ["three", "four"]
}
}
}
您控制生命周期和对象的方式不是此UI框架中的模式。VieModel不会神奇地重新发布单例值类型;它将使用该值进行实例化,然后在不再次签入共享实例的情况下改变其状态,除非重新构建它
类存储:ObservableObject{
静态变量共享:Store=.init()
结构ContentView:View{
@ObservedObject var store=store.shared
有很多潜在的可行模式。例如:
创建class RootStore:ObservableObject
并将其作为@StateObject存放在应用程序中。StateObject的生存期是该视图层次结构的生存期。您可以通过以下方式将其(a)直接公开给子视图。environmentObject(store)或(b)创建一个容器对象,例如提供ViewModels的工厂,并通过环境传递它,而不向视图公开存储,或者(c)同时执行a和b
如果您在另一个类中引用存储,请保持弱引用弱变量存储:存储?
。如果您在@Published上保持该根存储的状态,则可以直接订阅该发布服务器或根存储自己的ObjectWillChangePublisher。您还可以使用其他组合发布服务器,如CurrentValueSubject来实现与@Published相同
有关代码示例,请参见
struct ItemDetailView: View {
@ObservedObject var viewModel = ItemDetailViewModel()
class ViewModel: ObservableObject {
@Published var items: [String] = Store.shared.items