SwiftUI父视图StateObject传入子视图模型后数据为空问题排查
问题分析与解决:SwiftUI子ViewModel无法获取父ViewModel更新后的数据
问题原因
- 子视图
onAppear触发时机过早:子视图UnitStatusCard的onAppear会在视图显示时立刻调用getStatusMeta,但此时API请求尚未完成,父ViewModel的models还是初始的空数组。即便后续父ViewModel更新了models,getStatusMeta也不会自动重新执行。 - 未监听父ViewModel的属性变化:子ViewModel仅持有父ViewModel的引用,但没有监听
@Published修饰的models属性。当父ViewModel通过API填充models后,子ViewModel无法感知到这一变化,自然无法获取最新数据。
解决办法
方案一:子ViewModel监听父ViewModel的models变化
通过Combine框架监听父ViewModel中models的更新,自动触发数据处理逻辑:
import Combine class UnitStatusCardViewModel: ObservableObject { @Published var value: String = "Good" let devicesVM: DevicesViewModel private var cancellables = Set<AnyCancellable>() init(devicesVM: DevicesViewModel) { self.devicesVM = devicesVM // 监听models的变化,数据就绪后执行处理逻辑 devicesVM.$models .receive(on: DispatchQueue.main) .filter { !$0.isEmpty } .sink { [weak self] _ in self?.getStatusMeta() } .store(in: &cancellables) } public func getStatusMeta() { print(devicesVM.models) // 可获取更新后的数据 // 在这里添加数据处理逻辑,比如更新value } }
同时移除子视图中onAppear的调用:
struct UnitStatusCard: View { @StateObject var unitStatusCardVM: UnitStatusCardViewModel init(devicesVM: DevicesViewModel) { self._unitStatusCardVM = StateObject(wrappedValue: UnitStatusCardViewModel(devicesVM: devicesVM)) } var body: some View { StatusView() } }
方案二:父ViewModel数据就绪后再渲染子视图
在根视图中判断models是否为空,仅当数据加载完成后才渲染子视图,确保子视图出现时数据已存在:
struct RootView: View { @StateObject private var devices = DevicesViewModel() var body: some View { ScrollView { if !devices.models.isEmpty { UnitStatusCard(devicesVM: devices) } else { ProgressView("加载中...") // 可选的加载提示 } } .onAppear() { devices.fetchDevices() } } }
此时子视图的onAppear触发时,models已经填充了API返回的数据,getStatusMeta能打印出正确结果。
方案三:父ViewModel主动通知子ViewModel刷新
在根视图中监听父ViewModel的models变化,主动调用子ViewModel的方法更新数据:
struct RootView: View { @StateObject private var devices = DevicesViewModel() @StateObject private var unitStatusVM = UnitStatusCardViewModel(devicesVM: DevicesViewModel()) var body: some View { ScrollView { UnitStatusCard(unitStatusCardVM: unitStatusVM) } .onAppear() { devices.fetchDevices() } .onChange(of: devices.models) { newModels in if !newModels.isEmpty { unitStatusVM.devicesVM = devices // 更新子ViewModel的父引用 unitStatusVM.getStatusMeta() } } } } // 修改子视图的初始化方式 struct UnitStatusCard: View { @ObservedObject var unitStatusCardVM: UnitStatusCardViewModel init(unitStatusCardVM: UnitStatusCardViewModel) { self.unitStatusCardVM = unitStatusCardVM } var body: some View { StatusView() } }
内容的提问来源于stack exchange,提问作者CCCC
相关产品推荐
相关产品推荐

