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

SwiftUI应用如何确保同一时间仅存在一个主窗口实例?

解决SwiftUI MenuBarExtra打开主窗口重复创建的问题

默认的WindowGroup设计允许创建多个同ID的窗口实例,这就是点击按钮时会新建窗口而非激活已有窗口的原因。以下两种方案可以确保同一时间仅存在一个"MainWindow"实例:

方案一:使用单实例Window(macOS 13+)

从macOS 13开始,SwiftUI提供了Window场景,本身就是单实例设计,替换原有的WindowGroup即可:

@main
struct MyApp : App
{
    @Environment(\.openWindow) private var openWindow
    @Environment(\.activateWindow) private var activateWindow
    
    @AppStorage(SettingsKeys.showInMenuBar) var showInMenuBar = false
    
    @StateObject var viewModel = MainViewModel()
    
    var body: some Scene {
        Window("MainWindow", id: "MainWindow") {
            ContentView(viewModel: self.viewModel)
        }
        .windowToolbarStyle(.unified)
        
        MenuBarExtra("ScannerMainItem", systemImage: "rectangle.stack", isInserted: $showInMenuBar) {
            Button(LocalizedStringKey("OPEN_MAIN_WINDOW")) {
                // 优先激活已有窗口,失败则创建新窗口
                if !activateWindow(id: "MainWindow") {
                    openWindow(id: "MainWindow")
                }
            }.keyboardShortcut("m")
            
            Divider()
            
            Button(LocalizedStringKey("QUIT")) {
                NSApplication.shared.terminate(nil)
            }.keyboardShortcut("q")
        }
    }
}

activateWindow会尝试激活对应ID的已存在窗口,返回布尔值表示操作是否成功;如果窗口已关闭,激活失败,再调用openWindow创建新窗口。

方案二:兼容旧版本(macOS 12及以下)

如果需要支持macOS 12,直接通过AppKit API查找并激活主窗口,不存在则创建:

@main
struct MyApp : App
{
    @Environment(\.openWindow) private var openWindow
    
    @AppStorage(SettingsKeys.showInMenuBar) var showInMenuBar = false
    
    @StateObject var viewModel = MainViewModel()
    
    var body: some Scene {
        WindowGroup(id: "MainWindow") {
            ContentView(viewModel: self.viewModel)
        }
        .windowToolbarStyle(.unified)
        
        MenuBarExtra("ScannerMainItem", systemImage: "rectangle.stack", isInserted: $showInMenuBar) {
            Button(LocalizedStringKey("OPEN_MAIN_WINDOW")) {
                // 遍历窗口列表查找目标窗口
                if let mainWindow = NSApplication.shared.windows.first(where: {
                    $0.identifier?.rawValue == "MainWindow"
                }) {
                    // 激活窗口并前置显示
                    mainWindow.makeKeyAndOrderFront(nil)
                    NSApp.activate(ignoringOtherApps: true)
                } else {
                    // 窗口不存在时创建新窗口
                    openWindow(id: "MainWindow")
                }
            }.keyboardShortcut("m")
            
            Divider()
            
            Button(LocalizedStringKey("QUIT")) {
                NSApplication.shared.terminate(nil)
            }.keyboardShortcut("q")
        }
    }
}

通过遍历NSApplication.shared.windows匹配目标窗口ID,找到后激活并前置;未找到则调用openWindow创建新窗口,彻底避免重复实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 07:44:54