SwiftUI多层嵌套Published对象变更时视图未响应更新的问题求助
这不是SwiftUI的bug,而是因为ObservedObject只会监听第一层@Published属性的引用变化:当你修改嵌套对象(RecordingTimeManager)内部的@Published属性时,外层的AppStatus并没有发出对象变化的通知,所以SwiftUI视图不会重新渲染。
具体来说,你的AppStatus里的recordingTimeManager是一个@Published对象,但只有当你把它替换成一个新的RecordingTimeManager实例时,AppStatus的objectWillChange才会触发。而你只是修改了现有实例内部的maxRecordingTime,外层的AppStatus不会感知到这个变化,自然不会更新视图。另外你的代码里还有个小疏漏:RecordingTimeManager没有遵循ObservableObject协议,这是内部@Published属性生效的前提。
这里提供两种可行的解决方式,你可以根据项目结构选择:
方案1:让嵌套对象的变化传递到外层ObservableObject
先给RecordingTimeManager补上ObservableObject协议,再在AppStatus中订阅嵌套对象的变化事件,手动触发自身的更新通知:
// 修正RecordingTimeManager,添加ObservableObject协议 class RecordingTimeManager: ObservableObject { @Published var maxRecordingTime: TimeSeletedTime = .sixteenSeconds // ...其他代码 } // 修改AppStatus,传递嵌套对象的变化通知 class AppStatus: ObservableObject { @Published var recordingTimeManager: RecordingTimeManager = RecordingTimeManager() private var cancellables = Set<AnyCancellable>() init() { // 当recordingTimeManager内部属性变化时,触发AppStatus的对象更新 recordingTimeManager.objectWillChange .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) } }
这样修改后,当你更新maxRecordingTime时,RecordingTimeManager的变化会传递给AppStatus,最终触发SwiftUI视图重新渲染,按钮的前景色就能正常更新了。
方案2:在视图中直接监听嵌套对象
如果你不想改动AppStatus的结构,可以在SwiftUI视图里把recordingTimeManager单独作为ObservedObject来监听:
struct YourRecordingView: View { @ObservedObject var appStatus: AppStatus // 单独监听嵌套的RecordingTimeManager @ObservedObject var timeManager: RecordingTimeManager init(appStatus: AppStatus) { self.appStatus = appStatus self.timeManager = appStatus.recordingTimeManager } var body: some View { Button { timeManager.maxRecordingTime = .fifteenSeconds print("15 \(timeManager.maxRecordingTime)") } label: { Text("15") .font(Font.custom("BwGradual-Bold", size: 15)) .foregroundColor(timeManager.maxRecordingTime == .fifteenSeconds ? CLAPSOFFWHITE : TRIBESGREY) } } }
这种方式让视图直接绑定到RecordingTimeManager的变化,当内部属性更新时,视图会自动重新渲染。
- 所有包含@Published属性的类必须遵循
ObservableObject协议,否则@Published不会生效。 - 方案1中使用Combine的
sink时,一定要用[weak self]避免循环引用,同时用cancellables存储订阅者,防止订阅被提前释放。
内容的提问来源于stack exchange,提问作者jmrueda

