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

@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:

  1. 修改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
        }
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:18:30