SwiftUI中@EnvironmentObject值变更时保留ViewModel值的方法
解决SwiftUI中子视图状态因父视图环境对象变更重置的问题
问题原因
你的ChildView是结构体类型,当valueComingFromParent(环境对象)的属性变更时,ChildView的body会触发重计算,结构体实例被重新创建。而你在ChildView内部直接初始化@ObservedObject var viewModel = ChildViewModel(),每次结构体重建都会生成新的ChildViewModel实例,导致counter被重置为初始值0。
解决方案
方案1:使用@StateObject替代@ObservedObject(iOS 14+)
@StateObject是SwiftUI专门用来在View生命周期内保留对象实例的属性包装器,即使View结构体重建,它持有的对象也不会被重新初始化。修改代码如下:
struct ChildView: View { // 替换@ObservedObject为@StateObject @StateObject var viewModel = ChildViewModel() @EnvironmentObject var valueComingFromParent: SomeOtherViewModel var body: some View { VStack { Button("Toast", action: { valueComingFromParent.toast.toggle() }) Button("Incrementer \(viewModel.counter)", action: { viewModel.counter += 1 }) } } }
方案2:从父视图传递ViewModel(兼容iOS 13)
如果需要兼容iOS 13,将ChildViewModel的实例创建权交给父视图,通过参数传递给子视图,这样子视图重建时不会重新创建ViewModel实例:
父视图代码:
struct ParentView: View { // 父视图持有ChildViewModel实例 @StateObject var childViewModel = ChildViewModel() @StateObject var parentViewModel = SomeOtherViewModel() var body: some View { ChildView(viewModel: childViewModel) .environmentObject(parentViewModel) } }
子视图修改后代码:
struct ChildView: View { // 通过参数接收父视图传递的ViewModel @ObservedObject var viewModel: ChildViewModel @EnvironmentObject var valueComingFromParent: SomeOtherViewModel var body: some View { VStack { Button("Toast", action: { valueComingFromParent.toast.toggle() }) Button("Incrementer \(viewModel.counter)", action: { viewModel.counter += 1 }) } } }
两种方案都能保证viewModel.counter在valueComingFromParent变更时保留当前值,你可以根据iOS版本需求选择合适的方案。
内容的提问来源于stack exchange,提问作者Nabeel
相关产品推荐
相关产品推荐

