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

SwiftUI如何从AppDelegate等外部访问@State变量的真实值

问题原因
  • @State 是SwiftUI专为View结构体设计的属性包装器,它的实际存储由SwiftUI框架在渲染生命周期中托管,并不保存在你手动初始化的ContentView结构体实例中。
  • ContentView是值类型,你在AppDelegate中持有的实例,和NSHostingView加载渲染时使用的实例是两个独立的拷贝,渲染过程中修改的@State值不会同步到你最初持有的那个ContentView实例上,所以读取永远是初始值60。
  • 不要在View结构体的外部直接访问@State、@Binding这类SwiftUI专用属性包装器的存储值,它们仅在SwiftUI的渲染上下文中有效。
解决方案

将共享状态抽离到独立的ObservableObject类中管理,AppDelegate和ContentView共同持有/观察同一个实例,即可实现状态同步。

步骤1:创建共享状态类

import SwiftUI

class TimerSettings: ObservableObject {
    // @Published会自动通知观察者数据变化
    @Published var seconds: String = "60"
}

步骤2:修改AppDelegate代码

class AppDelegate: NSObject, NSApplicationDelegate {
    // 全局持有共享状态实例
    var timerSettings = TimerSettings()
    var window: NSWindow?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        launchTimer()
    }

    func launchTimer() {
        // 直接从共享状态读最新的seconds值即可
        print(timerSettings.seconds)
        let timer = DispatchTimeInterval.seconds(Int(timerSettings.seconds) ?? 0)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + timer) {
            self.launchTimer()
        }
    }
    
    @objc func showPreferenceWindow(_ sender: Any?) {
        if(window != nil) {
            window.close()
        }
        
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.isReleasedWhenClosed = false
        // 初始化ContentView时传入共享状态实例
        window.contentView = NSHostingView(rootView: ContentView(settings: timerSettings))
        
        window.center()
        window.makeKeyAndOrderFront(nil)
        NSApplication.shared.activate(ignoringOtherApps: true)
    }
}

步骤3:修改ContentView代码

struct ContentView: View {
    // 观察传入的共享状态实例
    @ObservedObject var settings: TimerSettings

    var body: some View {
        TextField("60", text: $settings.seconds)
    }
}
可选优化

如果你的App有多个层级的View都需要访问这个配置,可以将TimerSettings注入到环境变量中,不用逐层传递参数:
在创建NSHostingView时添加.environmentObject(timerSettings)修饰符,ContentView中改用@EnvironmentObject var settings: TimerSettings接收即可。

内容的提问来源于stack exchange,提问作者Maxime Dupré

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 11:36:05