SwiftUI应用如何确保同一时间仅存在一个主窗口实例?
默认的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
相关产品推荐
相关产品推荐

