SwiftUI中父子视图模型间传递数据的最优方案
父子视图模型间数据传递的最佳方案(不用EnvironmentObject)
针对你这种仅父子视图间传递数据的场景,以下是几种实用的实现方案,按需选择即可:
方案1:闭包回调(最推荐,轻量解耦)
核心思路是给子视图模型添加一个回调闭包,当子模型的选中数据变化时,主动把新值传递给父模型。
修改子视图模型:
class ChildViewModel: ObservableObject { @Published var selectedExercise = [String]() // 定义回调闭包,用于传递选中数据 var onSelectionChanged: (([String]) -> Void)? init() { // 监听selectedExercise的变化,触发闭包 $selectedExercise .sink { [weak self] newSelection in self?.onSelectionChanged?(newSelection) } .store(in: &cancellables) } private var cancellables = Set<AnyCancellable>() }
父视图中初始化子模型并绑定回调:
struct ParentView: View { @StateObject private var parentVM = ParentViewModel() @StateObject private var childVM: ChildViewModel init() { let childVM = ChildViewModel() // 设置回调,更新父模型的数据 childVM.onSelectionChanged = { newSelection in self.parentVM.selectedExercise = newSelection } self._childVM = StateObject(wrappedValue: childVM) } var body: some View { VStack { Text("父视图选中:\(parentVM.selectedExercise.joined(separator: ", "))") // 嵌入子视图,传入子模型 ChildView(viewModel: childVM) } } } struct ChildView: View { @ObservedObject var viewModel: ChildViewModel var body: some View { // 示例交互:点击按钮添加选中项 Button("添加练习") { viewModel.selectedExercise.append("练习\(Int.random(in: 1...10))") } } }
优点:子模型无需知道父模型的具体类型,解耦性强;实现简单,完全适配仅父子间传递的场景。
方案2:子模型持有父模型的弱引用
让子模型直接持有父模型的弱引用,当自身数据变化时,主动更新父模型的对应属性。
修改子视图模型:
class ChildViewModel: ObservableObject { @Published var selectedExercise = [String]() { didSet { // 数据变化时同步到父模型 parentVM?.selectedExercise = selectedExercise } } // 弱引用父模型,避免循环引用 weak var parentVM: ParentViewModel? init(parentVM: ParentViewModel?) { self.parentVM = parentVM } }
父视图中初始化子模型:
struct ParentView: View { @StateObject private var parentVM = ParentViewModel() @StateObject private var childVM: ChildViewModel init() { self._childVM = StateObject(wrappedValue: ChildViewModel(parentVM: parentVM)) } var body: some View { VStack { Text("父视图选中:\(parentVM.selectedExercise.joined(separator: ", "))") ChildView(viewModel: childVM) } } }
优点:代码更简洁,无需额外的Combine订阅;缺点:子模型和父模型耦合度高,灵活性稍差。
方案3:父模型订阅子模型的Published属性
利用Combine框架,让父模型订阅子模型的selectedExercise变化,自动同步自身数据。
修改父视图模型:
class ParentViewModel: ObservableObject { @Published var selectedExercise = [String]() let childVM: ChildViewModel private var cancellables = Set<AnyCancellable>() init() { self.childVM = ChildViewModel() // 订阅子模型的选中数据变化,同步到自身 childVM.$selectedExercise .assign(to: &$selectedExercise) } }
父视图中直接使用父模型持有的子模型:
struct ParentView: View { @StateObject private var parentVM = ParentViewModel() var body: some View { VStack { Text("父视图选中:\(parentVM.selectedExercise.joined(separator: ", "))") ChildView(viewModel: parentVM.childVM) } } }
优点:父模型主动管理数据同步,逻辑集中;缺点:父模型需要持有子模型实例,耦合度比闭包方案高。
总结推荐
如果追求低耦合、高灵活性,优先选闭包回调方案;如果场景简单、想精简代码,可选弱引用父模型方案;Combine订阅适合需要更复杂数据流处理的场景。
内容的提问来源于stack exchange,提问作者Shin Shan
相关产品推荐
相关产品推荐

