SwiftUI中子视图拥有的@StateObject如何在兄弟视图作为@ObservedObject使用
问题根源
你尝试直接从ChildView结构体实例中读取@StateObject属性的方式不符合SwiftUI的数据流规则:@StateObject的底层存储只有在视图被挂载到视图树之后才会被SwiftUI统一管理,你在父视图body内直接创建ChildView实例就访问其stateObject属性,拿到的仅为初始值,后续@Published属性的更新通知不会被SiblingView接收。
你修改的SiblingView初始化方法需要接收ObservedObject<T>类型,直接访问childView.stateObject拿到的是MySharedStateObject实例,$stateObject拿到的是ObservedObject<T>.Wrapper(用于Binding传递),自然会类型不匹配。即使你通过childView._stateObject拿到ObservedObject<T>实例(属于私有API,不推荐生产环境使用),也不能解决根本问题。
最优解决方案
将共享状态的持有权下放到包裹ChildView和SiblingView的独立容器视图中,父视图ParentView完全不持有这个共享状态,就不会触发全局重绘,StaticView自然不会被意外渲染。
具体修改步骤如下:
- 新增独立的容器视图,负责持有共享的
@StateObject,并分别传给两个子视图
struct ChildSiblingContainer: View, Identifiable { let id: Int @StateObject private var stateObject: MySharedStateObject init(initialValue: Int, id: Int) { self.id = id _stateObject = StateObject(wrappedValue: MySharedStateObject(initialValue: initialValue)) print("ChildSiblingContainer\(id) - init") } var body: some View { VStack { ChildView(stateObject: stateObject, id: id) .padding(5) SiblingView(observedStateObject: stateObject, id: id) .padding(5) } } }
- 修改
ChildView,将@StateObject改为@ObservedObject,由容器传入共享状态
struct ChildView: View, Identifiable { @ObservedObject var stateObject: MySharedStateObject var id: Int init(stateObject: MySharedStateObject, id: Int) { self.id = id self.stateObject = stateObject print("ChildView\(id) - init") } var body: some View { if printWithinBody("ChildView\(id) - rendering body\n") { Text("Child\(id) owned val \(stateObject.sharedValue)") .frame(width: 200, height: 30, alignment: .center) .padding(5) .border(.green, width: 2) .onTapGesture { stateObject.sharedValue += 1 } } } }
- 保留你最初版本的
SiblingView实现即可,不需要修改初始化方法 - 修改
ParentView的body,直接使用容器视图替换原来的VStack逻辑
var body: some View { if printWithinBody("ParentView rendering body\n") { VStack { HStack { ForEach((1...3), id: \.self) { StaticView(id: $0) .padding(5) } } HStack { ForEach((1...3), id: \.self) { viewID in ChildSiblingContainer(initialValue: Int.random(in: 1...5), id: viewID) } } } } }
效果验证
点击任意ChildView修改值时,控制台只会打印对应ID的ChildView、SiblingView的渲染日志,StaticView和其他ID的视图完全不会被重绘,完全符合需求。
内容的提问来源于stack exchange,提问作者panzon
相关产品推荐
相关产品推荐

