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

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)

适合复杂项目中多个视图共享状态的场景:

  1. 在App入口注入ViewModel到环境中(示例如下):
@main
struct YourAppName: App {
    let settingsVM = SetttingsViewModel()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(settingsVM)
        }
    }
}
  1. 修改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)
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 12:15:24