SwiftUI List在ViewModel变更时不更新的问题排查
问题分析
你的核心问题是MyListView的@StateObject viewModel只会在视图首次初始化时创建,当切换新的content时,视图实例没有被替换,导致viewModel仍然持有旧的content数据,自然不会更新列表。而添加.id(content.id)会强制SwiftUI销毁旧视图并创建新实例,虽然能更新数据,但会重置@State修饰的sortingMode。
解决方案
方案1:让ListViewModel支持动态更新content
修改ListViewModel,添加重新加载数据的方法,再在MyListView中监听content的变化,触发数据更新:
class ListViewModel: ObservableObject { @Published var features: [Feature] = [] private var cancellables = Set<AnyCancellable>() init(content: DataContainer) { loadContent(content) } // 新增重新加载方法 func loadContent(_ content: DataContainer) { content.loadContents() .assign(to: &$features) } } struct MyListView: View { @StateObject var viewModel: ListViewModel @State var sortingMode = .alphabetical var content: DataContainer // 新增属性用于监听变化 init(content: DataContainer) { _viewModel = StateObject(wrappedValue: ListViewModel(content: content)) self.content = content } var body: some View { List { ForEach(viewModel.features, id: \.id) { FeatureRow(feature: $0) } } .onChange(of: content) { newContent in viewModel.loadContent(newContent) } } }
方案2:将DataContainer改为ObservableObject,让viewModel自动响应
如果DataContainer的内容变化需要被监听,可以将其改为ObservableObject,让viewModel自动响应内容更新:
class DataContainer: Identifiable, ObservableObject { // 若有需要对外通知的属性,可添加@Published修饰 func loadContents() -> AnyPublisher<[Feature], Never> { ... } } class ListViewModel: ObservableObject { @Published var features: [Feature] = [] private var cancellables = Set<AnyCancellable>() init(content: DataContainer) { refreshFeatures(content) // 监听DataContainer的变化,自动刷新数据 content.objectWillChange .sink { [weak self] in self?.refreshFeatures(content) } .store(in: &cancellables) } private func refreshFeatures(_ content: DataContainer) { content.loadContents() .assign(to: &$features) } }
方案3:使用@ObservedObject替代@StateObject(简单直接)
把viewModel改为@ObservedObject,在DetailView中创建并传递,这样每次content变化时都会生成新的viewModel,同时保留sortingMode状态:
struct DetailView: View { @State var isChartSelected = false var content: DataContainer var body: some View { if isChartSelected { MyChartView(content: content) } else { MyListView(viewModel: ListViewModel(content: content)) } } } struct MyListView: View { @ObservedObject var viewModel: ListViewModel @State var sortingMode = .alphabetical // 移除自定义初始化方法,使用默认实现 var body: some View { List { ForEach(viewModel.features, id: \.id) { FeatureRow(feature: $0) } } } }
关键原理
@StateObject的生命周期与视图实例绑定,只有视图被销毁重建时才会重新初始化。onChange(of:)可以在不重建视图的前提下,监听属性变化并触发逻辑更新。- 避免使用
.id()强制重建视图,否则会丢失视图内部的状态变量。
内容的提问来源于stack exchange,提问作者RL2000
相关产品推荐
相关产品推荐

