@AppStorage在EnvironmentObject中初始化异常且无法持久化修改问题
在EnvironmentObject中正确使用@AppStorage的解决方案
问题根源
@AppStorage 属性包装器是为View层级设计的,它需要依赖View的环境来访问UserDefaults。当你把它放在ObservableObject类内部时,初始化阶段无法正确关联到App的UserDefaults环境,导致默认值失效(读取到0),且修改后的值无法持久化到UserDefaults中。
解决方案一:手动结合UserDefaults与@Published
直接在CaptureDelegate中用@Published属性管理状态,手动处理UserDefaults的读写逻辑,既保证UI响应式更新,又能实现持久化:
class CaptureDelegate: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject { @Published var captureInterval: Int { didSet { UserDefaults.standard.set(captureInterval, forKey: "captureInterval") } } @Published var startCaptureInterval: Int { didSet { UserDefaults.standard.set(startCaptureInterval, forKey: "startCaptureInterval") } } override init() { // 初始化时优先读取UserDefaults的值,无值则用默认值 let savedCaptureInterval = UserDefaults.standard.integer(forKey: "captureInterval") self.captureInterval = savedCaptureInterval == 0 ? 2 : savedCaptureInterval let savedStartInterval = UserDefaults.standard.integer(forKey: "startCaptureInterval") self.startCaptureInterval = savedStartInterval == 0 ? 3 : savedStartInterval super.init() } }
注:因为UserDefaults.standard.integer(forKey:)默认返回0,所以需要判断是否为0来使用预设的默认值,避免首次启动时被默认0覆盖。
解决方案二:将@AppStorage放在View层级并同步到ObservableObject
如果坚持使用@AppStorage,可以将其放在View层级(比如App结构体或CameraView),再通过绑定同步到CaptureDelegate:
- 修改App结构体:
@main struct xxxxxx: App { @AppStorage("isOnboarding") var isOnboarding: Bool = true @AppStorage("captureInterval") var captureInterval: Int = 2 @AppStorage("startCaptureInterval") var startCaptureInterval: Int = 3 @Environment(\.scenePhase) var scenePhase @State var showingPermissionAlert: Bool = false @StateObject var captureDelegate: CaptureDelegate init() { // 初始化时传递正确的初始值 let initCaptureInterval = UserDefaults.standard.integer(forKey: "captureInterval") == 0 ? 2 : UserDefaults.standard.integer(forKey: "captureInterval") let initStartInterval = UserDefaults.standard.integer(forKey: "startCaptureInterval") == 0 ? 3 : UserDefaults.standard.integer(forKey: "startCaptureInterval") _captureDelegate = StateObject(wrappedValue: CaptureDelegate(captureInterval: initCaptureInterval, startCaptureInterval: initStartInterval)) } var body: some Scene { WindowGroup { if isOnboarding { OnBoardingView() } else { CameraView( captureInterval: $captureInterval, startCaptureInterval: $startCaptureInterval ) .environmentObject(captureDelegate) } } // 监听@AppStorage变化,同步到CaptureDelegate .onChange(of: captureInterval) { newValue in captureDelegate.captureInterval = newValue } .onChange(of: startCaptureInterval) { newValue in captureDelegate.startCaptureInterval = newValue } } }
- 修改CaptureDelegate:
class CaptureDelegate: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject { @Published var captureInterval: Int @Published var startCaptureInterval: Int init(captureInterval: Int, startCaptureInterval: Int) { self.captureInterval = captureInterval self.startCaptureInterval = startCaptureInterval super.init() } }
方案推荐
优先选择解决方案一,它将数据持久化逻辑封装在ObservableObject内部,让View只负责交互展示,代码结构更清晰,也避免了跨层级的同步逻辑。
内容的提问来源于stack exchange,提问作者Milan Labus
相关产品推荐
相关产品推荐

