You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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自然不会被意外渲染。
具体修改步骤如下:

  1. 新增独立的容器视图,负责持有共享的@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)
        }
    }
}
  1. 修改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
                }
        }
    }
}
  1. 保留你最初版本的SiblingView实现即可,不需要修改初始化方法
  2. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 11:39:03