SwiftUI中MenuBarExtra应用如何设置Settings视图窗口层级?
问题分析
- 设置窗口后置:使用
NSApp.sendAction(Selector(("showSettingsWindow:")))唤起设置窗口时,未明确指定窗口层级和前置状态,导致窗口被其他应用遮挡。 - 关闭设置窗口后MenuBarExtra消失:全局设置所有窗口为
.floating后,关闭设置窗口时应用失去所有活跃窗口,系统将应用转入后台并隐藏MenuBarExtra,但进程仍保持运行。
解决方案
1. 规范唤起设置窗口的方式
使用SwiftUI官方提供的OpenSettingsAction(macOS 13+ / iOS 16+)替代旧的Selector调用,更安全且符合框架规范。
2. 单独设置设置窗口的层级
仅针对设置窗口设置.floating层级并强制前置,避免影响其他窗口的行为。
3. 确保应用无窗口时MenuBarExtra仍可见
设置应用的激活策略为.accessory,保证即使没有任何窗口,MenuBarExtra仍会保留在菜单栏中。
完整代码示例
import SwiftUI struct MainScene: Scene { var body: some Scene { WindowGroup(id: "mainscene") { Button("close") { NSApplication.shared.keyWindow?.close() } .task { NSApp.windows.first?.close() } } } } struct SettingView: View { var body: some View { Text("偏好设置页面") .frame(width: 300, height: 200) .onAppear { // 定位到设置窗口并调整层级 if let settingsWindow = NSApp.windows.first(where: { $0.className == "NSWindow" && $0.title.contains("Settings") }) { settingsWindow.level = .floating settingsWindow.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) } } } } struct Demo: App { init() { // 设置应用激活策略,确保无窗口时MenuBarExtra仍显示 NSApp.setActivationPolicy(.accessory) } var body: some Scene { MainScene() Settings { SettingView() } MenuBarExtra("Menu Bar", systemImage: "swift") { Button("偏好设置...") { // 使用官方API唤起设置窗口 openSettings() } Divider() Button("退出") { NSApplication.shared.terminate(nil) }.keyboardShortcut("q") } // 注入openSettings环境动作(macOS13+自动支持,此处显式声明兼容) .environment(\.openSettings, OpenSettingsAction { NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) }) } }
额外说明
- 如果你的项目需要兼容macOS 12及以下版本,可以保留
NSApp.sendAction(Selector(("showSettingsWindow:")))的调用方式,但需要在设置窗口出现后手动调整层级和前置状态。 .accessory激活策略会让应用不在Dock中显示,若需要Dock图标,可改用.regular策略,并确保至少有一个窗口始终存在(比如隐藏的主窗口)。
内容的提问来源于stack exchange,提问作者FaiChou
相关产品推荐
相关产品推荐

