SwiftUI单例类中@AppStorage值修改后需重启才生效的问题
问题原因
- @AppStorage 未触发 ObservableObject 变更通知:@AppStorage 本身是为直接在 SwiftUI 视图中使用设计的,会自动更新绑定的视图,但当它被放在 ObservableObject 单例内部时,不会主动触发单例的
objectWillChange发布者。这意味着即使message1的值被修改并保存到 UserDefaults,单例不会通知视图发生了变化。 - 视图未监听单例的变更事件:你的视图直接通过
Settings.shared.message1访问值,但没有通过@ObservedObject或@StateObject订阅单例的 ObservableObject 变更信号,所以即使单例后续发布了变更,视图也不会自动刷新。
解决办法
方法一:手动触发变更通知 + 视图监听单例
首先修改 Settings 类,在 @AppStorage 属性变更时手动发送通知;然后在视图中通过监听对象访问属性:
- 更新
Settings类:
final class Settings: ObservableObject { static let shared = Settings() private init() { } @AppStorage("message1") var message1: String = "" { didSet { // 手动发送变更通知,告知视图更新 objectWillChange.send() } } }
- 修改视图代码:
struct ContentView: View { @State private var isOnSettings = false // 用 @StateObject 监听单例(全局唯一,只会初始化一次) @StateObject private var settings = Settings.shared var body: some View { NavigationView { List { Text(settings.message1) } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Image(systemName: "gearshape.fill") .onTapGesture { isOnSettings.toggle() } } } } .sheet(isPresented: $isOnSettings) { SettingsView() } } } struct SettingsView: View { @StateObject private var settings = Settings.shared var body: some View { NavigationView { List { TextField("Type", text: $settings.message1) } } } }
方法二:用 UserDefaults + @Published 替代 @AppStorage
显式使用 UserDefaults 存储值,并用 @Published 标记属性,确保变更能被视图感知:
final class Settings: ObservableObject { static let shared = Settings() private init() { } private let defaults = UserDefaults.standard private let messageKey = "message1" @Published var message1: String { get { defaults.string(forKey: messageKey) ?? "" } set { defaults.set(newValue, forKey: messageKey) } } }
之后同样在视图中用 @StateObject 或 @ObservedObject 监听单例即可。
方法三:使用环境对象(EnvironmentObject)
如果多个视图需要访问设置,可以将单例注入为环境对象,避免重复声明监听:
- 在 App 结构体中注入:
@main struct YourApp: App { var body: some Scene { WindowGroup { ContentView() .environmentObject(Settings.shared) } } }
- 在视图中使用:
struct ContentView: View { @State private var isOnSettings = false @EnvironmentObject private var settings: Settings var body: some View { // 用 settings.message1 访问值 } } struct SettingsView: View { @EnvironmentObject private var settings: Settings var body: some View { // 用 $settings.message1 绑定输入 } }
内容的提问来源于stack exchange,提问作者MohAliBou
相关产品推荐
相关产品推荐

