SwiftUI UserDefaults绑定问题:ContentView无法实时更新状态求助
问题原因
你在ContentView和Settings视图中分别初始化了独立的SetttingsViewModel实例。当Settings里的视图修改ViewModel的settingActivated值时,只有它自己的ViewModel实例会更新并同步到UserDefaults,但ContentView持有的是另一个完全不同的ViewModel对象,它不会感知到这个变化——只有重启应用时,它才会重新从UserDefaults读取最新值,这就是主视图不实时更新的原因。
解决办法
下面提供几种可行的修复方案,你可以根据项目需求选择:
方案1:让ViewModel成为单例(最简单直接)
修改SetttingsViewModel,让它成为全局唯一的实例,确保所有视图共享同一个ViewModel对象:
class SetttingsViewModel: ObservableObject { // 全局单例实例 static let shared = SetttingsViewModel() @Published var settingActivated: Bool = UserDefaults.standard.bool(forKey: "settingActivated") { didSet { UserDefaults.standard.set(self.settingActivated, forKey: "settingActivated") } } // 私有化构造方法,防止外部创建新实例 private init() {} }
Settings视图修改:
struct Settings: View { // 使用单例实例 @ObservedObject var settingsVM = SetttingsViewModel.shared var body: some View { if settingsVM.settingActivated { Text("Setting activated") } else { Text("Setting deactivated") } Button("Activate") { settingsVM.settingActivated.toggle() } } }
ContentView修改:
struct ContentView: View { // 使用单例实例 @ObservedObject var settingsVM = SetttingsViewModel.shared @State private var showsettings = false var body: some View { VStack { if settingsVM.settingActivated { Text("Setting Activated") .padding(.top) } else { Text("Setting Deactivated") .padding(.top) } Button("Show Settings") { showsettings.toggle() } .sheet(isPresented: $showsettings) { Settings() } } .frame(width: 300, height: 300) } }
方案2:通过Sheet传递同一个ViewModel实例
如果不想用单例,可以把ContentView的ViewModel直接传递给Settings视图,确保两者使用同一个对象:
ContentView修改:
struct ContentView: View { @ObservedObject var settingsVM = SetttingsViewModel() @State private var showsettings = false var body: some View { VStack { if settingsVM.settingActivated { Text("Setting Activated") .padding(.top) } else { Text("Setting Deactivated") .padding(.top) } Button("Show Settings") { showsettings.toggle() } // 传递当前ViewModel给Settings视图 .sheet(isPresented: $showsettings) { Settings(settingsVM: settingsVM) } } .frame(width: 300, height: 300) } }
Settings视图修改:
struct Settings: View { // 接收ContentView传递的ViewModel实例 @ObservedObject var settingsVM: SetttingsViewModel var body: some View { if settingsVM.settingActivated { Text("Setting activated") } else { Text("Setting deactivated") } Button("Activate") { settingsVM.settingActivated.toggle() } } }
方案3:使用环境对象(EnvironmentObject)
适合复杂项目中多个视图共享状态的场景:
- 在App入口注入ViewModel到环境中(示例如下):
@main struct YourAppName: App { let settingsVM = SetttingsViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(settingsVM) } } }
- 修改ContentView引用环境对象:
struct ContentView: View { // 从环境中获取ViewModel @EnvironmentObject var settingsVM: SetttingsViewModel @State private var showsettings = false var body: some View { VStack { if settingsVM.settingActivated { Text("Setting Activated") .padding(.top) } else { Text("Setting Deactivated") .padding(.top) } Button("Show Settings") { showsettings.toggle() } .sheet(isPresented: $showsettings) { Settings() } } .frame(width: 300, height: 300) } }
- 修改Settings视图引用环境对象:
struct Settings: View { // 从环境中获取ViewModel @EnvironmentObject var settingsVM: SetttingsViewModel var body: some View { if settingsVM.settingActivated { Text("Setting activated") } else { Text("Setting deactivated") } Button("Activate") { settingsVM.settingActivated.toggle() } } }
额外注意点
- 你的ViewModel类名拼写为
SetttingsViewModel(三个t),建议统一修正为SettingsViewModel(两个t),避免后续出现拼写错误。 - macOS 10.15确实不支持
@AppStorage(它是iOS 14+/macOS 11+引入的),所以你用ViewModel结合UserDefaults的方案是正确的。
内容的提问来源于stack exchange,提问作者swiftnoob
相关产品推荐
相关产品推荐

