如何让NSStatusItem快捷键在未选中状态下正常生效?
解决无窗口状态栏应用快捷键失效问题
问题根源在于你的应用是无窗口、无Dock图标的LSUIElement类型应用,默认只有当状态栏菜单展开或应用处于活跃状态时,菜单绑定的快捷键才会触发。失去焦点后应用不再活跃,快捷键就无法被系统捕获。下面提供两种可行的解决方案:
方案一:创建不可见窗口维持事件接收能力
通过创建一个完全透明、无尺寸的不可见窗口,让应用能够持续接收系统键盘事件,菜单绑定的快捷键即可在任何状态下生效。
修改后的代码:
class AppDelegate: NSObject, NSApplicationDelegate { private lazy var statusItem: NSStatusItem = NSStatusItem() private var invisibleWindow: NSWindow! // 添加不可见窗口属性 func applicationDidFinishLaunching(_ aNotification: Notification) { // 初始化不可见窗口 invisibleWindow = NSWindow( contentRect: .zero, styleMask: .borderless, backing: .buffered, defer: false ) invisibleWindow.level = .statusBar invisibleWindow.isOpaque = false invisibleWindow.backgroundColor = .clear invisibleWindow.hidesOnDeactivate = false NSApp.activate(ignoringOtherApps: true) // 激活应用确保事件接收 // 原有状态栏图标初始化逻辑 statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusItem.button { button.image = NSImage(systemSymbolName: "figure.wave", accessibilityDescription: "1") } let printHello: NSMenuItem = NSMenuItem(title: "print Hello world!", action: #selector(printHelloAction) , keyEquivalent: "h") printHello.keyEquivalentModifierMask = .command // 明确指定Command修饰符 let quit: NSMenuItem = NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") quit.keyEquivalentModifierMask = .command let menu: NSMenu = NSMenu() menu.addItem(printHello) menu.addItem(quit) statusItem.menu = menu } @objc func printHelloAction() { print("Hello, world!") } }
方案二:注册全局快捷键监听器
直接通过系统全局事件监视器监听指定快捷键,无论应用是否活跃都能触发对应动作,无需依赖窗口或应用活跃状态。
修改后的代码:
class AppDelegate: NSObject, NSApplicationDelegate { private lazy var statusItem: NSStatusItem = NSStatusItem() func applicationDidFinishLaunching(_ aNotification: Notification) { // 原有状态栏图标初始化逻辑 statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusItem.button { button.image = NSImage(systemSymbolName: "figure.wave", accessibilityDescription: "1") } let printHello: NSMenuItem = NSMenuItem(title: "print Hello world!", action: #selector(printHelloAction) , keyEquivalent: "h") let quit: NSMenuItem = NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") let menu: NSMenu = NSMenu() menu.addItem(printHello) menu.addItem(quit) statusItem.menu = menu // 注册全局快捷键监听器 registerGlobalShortcuts() } private func registerGlobalShortcuts() { // 监听Command+H NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { [weak self] event in guard let self = self else { return } if event.modifierFlags.contains(.command), event.charactersIgnoringModifiers == "h" { self.printHelloAction() NSApp.preventWindowOrdering() // 阻止事件传递给其他应用 } } // 监听Command+Q NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { event in if event.modifierFlags.contains(.command), event.charactersIgnoringModifiers == "q" { NSApp.terminate(nil) NSApp.preventWindowOrdering() } } } @objc func printHelloAction() { print("Hello, world!") } }
注意事项
- 方案一的不可见窗口完全不会干扰用户操作,同时能保留菜单快捷键的原生逻辑。
- 方案二需注意:若其他应用注册了相同全局快捷键,可能出现冲突;部分系统级快捷键的触发优先级需依赖系统配置,但自身应用的退出动作通常可以正常执行。
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

