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é
相关产品推荐
相关产品推荐

