SwiftUI使用ObservableObject/@Published时点击按钮视图不更新
问题根因
这是SwiftUI ObservableObject 嵌套使用的经典问题,核心机制是:@Published 仅会在属性本身被重新赋值时,触发所属ObservableObject的更新通知,不会自动感知嵌套的、同样遵循ObservableObject的引用类型实例的内部属性变化。
你代码里的具体问题点:
MyData、User都是class引用类型,存储在DataBase的data、users数组里。当你调用db.data[0].array.append(...)时,修改的是数组下标指向的MyData实例内部属性,并没有给db.data属性本身重新赋值新数组,因此DataBase的objectWillChange事件根本不会触发,持有db的ShowView监听不到变化,自然不会刷新。- 你在
List中遍历拿到的user、data实例,没有显式标记为@ObservedObject,哪怕这些实例自身的@Published属性触发了更新,当前视图也没有订阅对应事件,不会触发重绘。 - 额外隐患:
ContentView中直接用let db = DataBase(...)创建实例,当视图发生重绘时会重新生成全新的DataBase对象,导致状态丢失,正式代码需要用@StateObject标记持有。
你提到的DispatchQueue.main.async主线程调度写法本身没有问题,本次视图不更新和线程逻辑无关。
修复方案
根据你的业务场景二选一即可:
方案1:保留class引用类型,补全订阅逻辑
适合需要跨多个位置共享同一份数据实例引用的场景:
- 首先修正
ContentView的实例持有方式,避免重复初始化:
struct ContentView: View { @StateObject var db = DataBase(data: data, users: users) var body: some View { ShowView(db: db) } }
- 在
List遍历子对象时,显式声明@ObservedObject订阅对应实例的更新:
List(db.users) { user in @ObservedObject var userItem = user Text("\(userItem.name) \(userItem.data.type)") Text("\(userItem.data.array.count)") Divider() } List(db.data) { dataItem in @ObservedObject var dataEntry = dataItem Text("\(dataEntry.type)") Text("\(dataEntry.array.count)") Divider() }
方案2:改用struct值类型,适配SwiftUI原生状态机制
如果不需要跨场景共享数据引用,这是更推荐的写法:把MyData、User从class改成struct,移除多余的ObservableObject遵循。struct是值类型,当你修改它的内部属性时,等价于给数组对应位置重新赋值了新的实例,会自动触发外层@Published的更新通知,完全不需要处理嵌套订阅逻辑:
struct MyData: Identifiable { let id = UUID() var type: String var array: [Double] init(type: String, array: [Double]) { self.type = type self.array = array } } struct User: Identifiable { var id: UUID = UUID() var name: String var data: MyData init(name: String, data: MyData) { self.name = name self.data = data } }
改完之后不需要修改视图里的其他逻辑,点击按钮就能正常触发视图更新。
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

