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

SwiftUI单例类中@AppStorage值修改后需重启才生效的问题

问题原因

  1. @AppStorage 未触发 ObservableObject 变更通知:@AppStorage 本身是为直接在 SwiftUI 视图中使用设计的,会自动更新绑定的视图,但当它被放在 ObservableObject 单例内部时,不会主动触发单例的 objectWillChange 发布者。这意味着即使 message1 的值被修改并保存到 UserDefaults,单例不会通知视图发生了变化。
  2. 视图未监听单例的变更事件:你的视图直接通过 Settings.shared.message1 访问值,但没有通过 @ObservedObject 或 @StateObject 订阅单例的 ObservableObject 变更信号,所以即使单例后续发布了变更,视图也不会自动刷新。

解决办法

方法一:手动触发变更通知 + 视图监听单例

首先修改 Settings 类,在 @AppStorage 属性变更时手动发送通知;然后在视图中通过监听对象访问属性:

  1. 更新 Settings 类:
final class Settings: ObservableObject {
    static let shared = Settings()
    private init() { }
    
    @AppStorage("message1") var message1: String = "" {
        didSet {
            // 手动发送变更通知,告知视图更新
            objectWillChange.send()
        }
    }
}
  1. 修改视图代码:
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)

如果多个视图需要访问设置,可以将单例注入为环境对象,避免重复声明监听:

  1. 在 App 结构体中注入:
@main
struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(Settings.shared)
        }
    }
}
  1. 在视图中使用:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 15:10:39