SwiftUI macOS应用:如何阻止WindowGroup重复生成实例,实现单窗口唤起/前置
SwiftUI macOS应用:如何阻止WindowGroup重复生成实例,实现单窗口唤起/前置
嘿,我完全懂你现在的困扰——每次点菜单栏的启动按钮就多出一个窗口,确实挺闹心的,而且你还不想碰AppKit,只想用纯SwiftUI解决对吧?别担心,我给你几个靠谱的纯SwiftUI方案:
方案一:用openWindow替代深链接,利用系统自带的单窗口逻辑
原来的深链接方式容易触发多窗口,换成SwiftUI原生的openWindow环境变量就可以解决,它支持指定窗口ID,当对应ID的窗口已存在时,系统会自动将其前置,而不是创建新窗口。
步骤1:给WindowGroup指定唯一ID
修改你的Power_ScheduleApp代码,给WindowGroup加上唯一标识,同时把窗口状态移到全局的AppState里方便共享:
import SwiftUI class AppState: ObservableObject { @Published var isMainWindowActive: Bool = false @Published var isMainWindowOpen = false // 把窗口状态移到这里,全局共享 } @main struct Power_ScheduleApp: App { @State private var menuBarExtraShown = true @StateObject private var appState = AppState() // 用StateObject持有全局状态 var body: some Scene { MenuBarExtra(isInserted: $menuBarExtraShown) { AppMenu(appState: appState) // 把状态传递给菜单 } label: { Label("", image: "psl") } .menuBarExtraStyle(.menu) // 给WindowGroup加上唯一ID,让系统识别这是同一个窗口 WindowGroup(id: "mainWindow") { ContentView() .frame(width: 450, height: 948) .fixedSize() .onAppear { print("Main window appeared") appState.isMainWindowOpen = true } .onDisappear { print("Main window disappeared") appState.isMainWindowOpen = false } } .windowResizability(.contentSize) .commands { if appState.isMainWindowOpen { CommandGroup(replacing: .newItem) { Button("New Window", action: {}) .disabled(true) .keyboardShortcut(KeyboardShortcut("n", modifiers: [.command])) } } else { EmptyCommands() } } } }
步骤2:修改菜单的唤起逻辑
把原来的深链接调用换成openWindow,不用手动判断窗口状态,系统会自动处理:
struct AppMenu: View { @Environment(\.openWindow) var openWindow // 引入openWindow环境变量 @Environment(\.dismiss) var dismiss @ObservedObject var appState: AppState func launchAppMenu() { // 直接调用openWindow,系统会自动判断:存在则前置,不存在则新建 openWindow(id: "mainWindow") dismiss() // 点击后自动关闭菜单 } func importMenu() { dismiss() } func quitAppMenu() { NSApplication.shared.terminate(nil) // 这里可以直接实现退出逻辑 } var body: some View { Button(action: launchAppMenu, label: { Text("Launch My App") }).keyboardShortcut("n") Button(action: importMenu, label: { Text("Import Configs") }).keyboardShortcut("i") Divider() Button(action: quitAppMenu, label: { Text("Quit My App") }).keyboardShortcut("q") } }
方案二:简化版(更清爽)
其实openWindow(id:)本身就自带单窗口逻辑,如果你不需要额外的状态追踪,甚至可以去掉isMainWindowOpen的判断,直接调用openWindow(id: "mainWindow")就行,系统会自动帮你处理前置/新建的逻辑,代码会更简洁。
补充说明
你原来做的“禁用新建窗口命令”的逻辑可以保留,配合上面的方案,就能彻底阻止用户通过任何方式多开窗口,完美实现单窗口的需求。
备注:内容来源于stack exchange,提问作者Paul Randall
相关产品推荐
相关产品推荐

