SwiftUI:如何通过UI(如Picker)更新ObservableObject中的Published值
更新说明:
本问题已解决。正确做法是通过投影ObservableObject来获取Binding,例如使用$options.refreshRate。
如何让SwiftUI的Picker(或其他依赖本地Binding的API)立即更新我的ObservedObject/EnvironmentObject?以下是详细背景...
我在开发每一款SwiftUI应用时都会遇到这样的需求:
- 创建一个存储用户偏好的类(命名为
Options),并将其设为ObservableObject。 - 所有需要被订阅的设置都用
@Published标记。 - 使用该设置的视图通过
@ObservedObject或@EnvironmentObject引入并订阅变更。
这套流程运行良好,但我始终面临一个问题:如何通过UI更新这些设置?通常我的做法如下:
- 创建如
OptionsPanel这样的SwiftUI视图,用于驱动Options类,让用户选择偏好设置。 - 假设有一个用枚举定义的选项:
enum RefreshRate { case low, medium, high }
自然地,我会选择SwiftUI的Picker来设置这个选项,但Picker的API要求选择参数必须是Binding。这就是问题所在...
为了让Picker正常工作,我通常会使用一个本地Binding,但实际上我并不关心这个本地值,而是希望选择变更后能立即向整个应用广播新值。ObservableObject(即Options类)本身可以很好地实现广播,但当前只是更新本地Binding,我需要找到每次变更时都能立即将Picker状态同步到ObservableObject的方法。
最初的思路理论上可行,但遇到了阻碍:
在SwiftUI视图中,我没有用最简单的@State来创建Binding,而是尝试用另一种初始化方式:
// 原本的写法... @ObservedObject var options: Options @State var refreshRate: RefreshRate = .medium // 尝试的写法... @ObservedObject var options: Options var refreshRate: Binding<RefreshRate>( get: { self.options.refreshRate }, set: { self.options.refreshRate = $0 } )
理论上这很棒!本地Binding直接关联到ObservableObject,Picker的所有变更都会立即广播到整个应用。
但这段代码无法运行,会报错:
Cannot use instance member 'options' within property initializer; property initializers run before 'self' is available
于是我用了一个糟糕的变通方案,虽然能运行,但架构很差:
让Options类提供一个静态的shared单例实例,然后在选项面板视图中这样写:
@ObservedObject var options: Options = .shared // <-- 仍需保留以让SwiftUI监听更新 var refreshRate: Binding<RefreshRate>( get: { Options.shared.refreshRate }, set: { Options.shared.refreshRate = $0 } )
在这个场景下这确实能运行,因为我只需要一个实例,只要始终引用这个单例就没问题,但这种架构并不合理。
那么...*有没有更好的解决方案?*这似乎是所有应用都会遇到的场景,肯定有更优的实现方式。
(我知道有人用.onDisappear来同步本地状态到ObservableObject,但这也不理想,因为我需要即时更新整个应用的状态。)
内容的提问来源于stack exchange,提问作者Justin Reusch

