Mac菜单栏应用全局禁用键盘输入的技术实现问询
解决方案:全局禁用Mac键盘输入(保留触控板/鼠标)
你的现有代码问题
你当前的代码仅创建并发送了一个虚拟键事件,并未对系统的键盘输入进行拦截,因此无法实现禁用效果。要全局禁用键盘,需通过CGEventTap拦截所有键盘事件并直接丢弃。
正确实现步骤
1. 申请辅助功能权限
Mac系统要求应用拥有辅助功能权限才能监听/拦截全局事件。你需要:
- 在应用的
Info.plist中添加NSAccessibilityUsageDescription键,填写权限用途说明(例如"需要权限以全局禁用键盘输入") - 引导用户前往系统偏好设置→「安全性与隐私」→「隐私」→「辅助功能」,启用你的应用
2. 实现CGEventTap拦截键盘事件
以下是完整Swift代码示例,实现全局禁用/启用键盘的功能:
import Cocoa class KeyboardDisabler: NSObject { private var eventTap: CFMachPort? private var runLoopSource: CFRunLoopSource? private var isKeyboardDisabled = false func toggleKeyboard() { isKeyboardDisabled.toggle() if isKeyboardDisabled { setupEventTap() NSLog("Keyboard Disabled") } else { removeEventTap() NSLog("Keyboard Enabled") } } private func setupEventTap() { guard eventTap == nil else { return } // 监听键盘按下/抬起事件 let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue) eventTap = CGEvent.tapCreate( tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: CGEventMask(eventMask), callback: { proxy, type, event, refcon in let disabler = unsafeBitCast(refcon, to: KeyboardDisabler.self) // 禁用状态下返回nil,直接丢弃键盘事件 return disabler.isKeyboardDisabled ? nil : event }, userInfo: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) ) guard let eventTap = eventTap else { NSLog("创建事件拦截失败,请检查辅助功能权限") return } // 将事件tap加入RunLoop,持续监听 runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0) if let runLoopSource = runLoopSource { CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) } } private func removeEventTap() { if let runLoopSource = runLoopSource { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) self.runLoopSource = nil } if let eventTap = eventTap { CFMachPortInvalidate(eventTap) self.eventTap = nil } } deinit { removeEventTap() } } // 使用示例(在菜单点击事件中调用) // let keyboardDisabler = KeyboardDisabler() // @objc func toggleKeyboardAction() { // keyboardDisabler.toggleKeyboard() // }
3. 关键说明
- 事件拦截逻辑:在
CGEventTap的回调中,当isKeyboardDisabled为true时返回nil,系统会直接丢弃该键盘事件,不会传递给任何应用处理。 - 权限处理:如果
CGEvent.tapCreate返回nil,几乎都是因为缺少辅助功能权限,需提示用户开启。 - 资源清理:在应用退出或不再需要拦截时,必须移除
eventTap并清理RunLoop资源,避免内存泄漏。
额外注意事项
- 部分系统级快捷键(如
Cmd+Q、Cmd+Tab)可能无法被完全拦截,这类事件属于系统更高层级的处理流程。若需要拦截此类快捷键,需额外处理,但常规场景下上述代码已能覆盖绝大多数键盘输入。 - 菜单栏应用默认后台运行,RunLoop设置需确保事件监听持续生效。
内容的提问来源于stack exchange,提问作者Craig Larkin
相关产品推荐
相关产品推荐

