如何向主结构体与AppDelegate传递StateObject及隐藏主窗口
问题1:共享AuthenticationViewModel实例给主结构体和AppDelegate
要解决实例不共享的问题,只需让AppDelegate持有同一个ViewModel引用,而非新建实例:
- 在
AppDelegate中添加可选的AuthenticationViewModel属性:
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { private var statusItem: NSStatusItem! private var popover: NSPopover! var authViewModel: AuthenticationViewModel? // 添加该属性 // 其余代码保持不变 }
- 在
GmailNotificationApp的body中,通过.onAppear将共享实例传递给appDelegate:
@main struct GmailNotificationApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate @StateObject var authViewModel = AuthenticationViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(authViewModel) .onAppear { appDelegate.authViewModel = authViewModel } } } }
- 修改
AppDelegate中创建popover的代码,使用共享实例:
func applicationDidFinishLaunching(_ notification: Notification) { // 其余代码保持不变 guard let authVM = authViewModel else { return } self.popover.contentViewController = NSHostingController(rootView: ContentView().environmentObject(authVM)) }
此时主结构体和AppDelegate将使用同一个AuthenticationViewModel实例。
问题2:仅保留菜单栏窗口,隐藏默认主窗口
推荐两种更合理的实现方案,替代临时空窗口的做法:
方式一:使用SwiftUI原生MenuBarExtra(更简洁)
直接用SwiftUI的MenuBarExtra替代AppDelegate手动创建的statusItem和popover,默认不会生成主窗口:
@main struct GmailNotificationApp: App { @StateObject var authViewModel = AuthenticationViewModel() var body: some Scene { MenuBarExtra("Gmail Notification", systemImage: "g.circle.fill") { ContentView() .environmentObject(authViewModel) .frame(width: 210, height: 300) } .menuBarExtraStyle(.window) // 模拟popover的显示效果 } }
这种方式无需自定义AppDelegate,系统会自动处理菜单栏图标和弹窗逻辑,且不会生成多余主窗口。
方式二:保留AppDelegate,禁用主窗口并设置为菜单栏模式
若需保留自定义AppDelegate逻辑,可按以下修改:
- 在
GmailNotificationApp中,将WindowGroup替换为空的Settings(无需偏好设置也可直接移除):
@main struct GmailNotificationApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate @StateObject var authViewModel = AuthenticationViewModel() var body: some Scene { Settings { EmptyView() // 避免生成默认主窗口 } } }
- 在
AppDelegate的applicationDidFinishLaunching中配置应用模式并关闭默认窗口:
func applicationDidFinishLaunching(_ notification: Notification) { // 设置应用为菜单栏模式,不在Dock显示 NSApplication.shared.setActivationPolicy(.accessory) // 关闭所有自动生成的窗口 NSApplication.shared.windows.forEach { $0.close() } // 原有的statusItem和popover创建代码保持不变,记得使用共享的authViewModel }
该方式让应用完全以菜单栏形式运行,不会出现主窗口,同时保留自定义弹窗逻辑。
内容的提问来源于stack exchange,提问作者CrayonApe
相关产品推荐
相关产品推荐

