SwiftUI中ObservableObject内Binding的问题及最优方案咨询
SwiftUI子ViewModel状态重置问题的解决方案
问题根源
当父视图的text(@State变量)发生变化时,ParentView的body会触发重绘。由于ChildView是值类型结构体,会被重新实例化,其初始化方法会再次执行,创建全新的ChildViewModel实例,导致原ViewModel中@Published修饰的value被重置为默认值false。
最优处理方式
方案一:子视图用@StateObject持有ViewModel(iOS 14及以上)
使用@StateObject替代@ObservedObject来管理ViewModel,它能保证ViewModel在视图的整个生命周期内仅初始化一次,即便子视图因父视图变化被重建,ViewModel实例也会被保留,状态不会丢失。
修改后的代码:
子视图
struct ChildView: View { @Binding var text: String @StateObject private var childViewModel: ChildViewModel init(text: Binding<String>) { self._text = text self._childViewModel = StateObject(wrappedValue: ChildViewModel(text: text)) } var body: some View { // 示例视图内容 TextField("输入文本", text: $text) Toggle("状态开关", isOn: $childViewModel.value) } }
ViewModel(无需修改)
class ChildViewModel: ObservableObject { @Published var value = false @Binding var text: String init(text: Binding<String>) { self._text = text } // 其他业务方法 }
方案二:父视图创建并管理ViewModel
将ViewModel的创建逻辑移至父视图,用@StateObject持有,再传递给子视图。这种方式让ViewModel的生命周期由父视图掌控,彻底避免子视图重绘导致的ViewModel重建。
修改后的代码:
父视图
struct ParentView: View { @State var text: String = "" @StateObject private var childViewModel: ChildViewModel init() { let textBinding = Binding<String>(get: { self.text }, set: { self.text = $0 }) self._childViewModel = StateObject(wrappedValue: ChildViewModel(text: textBinding)) } var body: some View { ChildView(childViewModel: childViewModel, text: $text) } }
子视图
struct ChildView: View { @ObservedObject var childViewModel: ChildViewModel @Binding var text: String var body: some View { // 示例视图内容 TextField("输入文本", text: $text) Toggle("状态开关", isOn: $childViewModel.value) } }
方案三:ViewModel移除@Binding,改用回调更新父状态
如果希望ViewModel与SwiftUI的Binding解耦,可以通过闭包回调的方式更新父视图的text状态,避免Binding变化间接引发的ViewModel重建问题。
修改后的代码:
ViewModel
class ChildViewModel: ObservableObject { @Published var value = false private let updateText: (String) -> Void var currentText: String init(currentText: String, updateText: @escaping (String) -> Void) { self.currentText = currentText self.updateText = updateText } func updateText(_ newText: String) { currentText = newText updateText(newText) } }
子视图
struct ChildView: View { @Binding var text: String @StateObject private var childViewModel: ChildViewModel init(text: Binding<String>) { self._text = text self._childViewModel = StateObject(wrappedValue: ChildViewModel( currentText: text.wrappedValue, updateText: { text.wrappedValue = $0 } )) } var body: some View { TextField("输入文本", text: $text) .onChange(of: text) { newText in childViewModel.currentText = newText } Toggle("状态开关", isOn: $childViewModel.value) } }
内容的提问来源于stack exchange,提问作者Tropicano
相关产品推荐
相关产品推荐

