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

如何向主结构体与AppDelegate传递StateObject及隐藏主窗口

问题1:共享AuthenticationViewModel实例给主结构体和AppDelegate

要解决实例不共享的问题,只需让AppDelegate持有同一个ViewModel引用,而非新建实例:

  1. 在AppDelegate中添加可选的AuthenticationViewModel属性:
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
    private var statusItem: NSStatusItem!
    private var popover: NSPopover!
    var authViewModel: AuthenticationViewModel? // 添加该属性
    
    // 其余代码保持不变
}
  1. 在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
                }
        }
    }
}
  1. 修改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逻辑,可按以下修改:

  1. 在GmailNotificationApp中,将WindowGroup替换为空的Settings(无需偏好设置也可直接移除):
@main
struct GmailNotificationApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
    @StateObject var authViewModel = AuthenticationViewModel()
    
    var body: some Scene {
        Settings {
            EmptyView() // 避免生成默认主窗口
        }
    }
}
  1. 在AppDelegate的applicationDidFinishLaunching中配置应用模式并关闭默认窗口:
func applicationDidFinishLaunching(_ notification: Notification) {
    // 设置应用为菜单栏模式,不在Dock显示
    NSApplication.shared.setActivationPolicy(.accessory)
    
    // 关闭所有自动生成的窗口
    NSApplication.shared.windows.forEach { $0.close() }
    
    // 原有的statusItem和popover创建代码保持不变,记得使用共享的authViewModel
}

该方式让应用完全以菜单栏形式运行,不会出现主窗口,同时保留自定义弹窗逻辑。


内容的提问来源于stack exchange,提问作者CrayonApe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:46:14