在macOS中,可以通过以下几种方法监听键盘的输入,实现快捷键调用方法,但各方法均有自身的利弊,需要使用者在开发时进行权衡。
监听快捷键方法
1、全局监听事件NSEvent.addGlobalMonitorForEvents
NSEvent.addGlobalMonitorForEvents(matching:handler:) 可以用来全局监听鼠标或键盘事件,即使 App 不在前台,也能收到这些事件。要全局监听快捷键,需要监听键盘事件(如 .keyDown),然后判断是否按下了特定组合键。
使用方法:
// 添加全局监听
NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { [weak self] event in
self?.handleGlobalKeyEvent(event)
}
// 处理监听事件
func handleGlobalKeyEvent(_ event: NSEvent) {
let commandPressed = event.modifierFlags.contains(.command)
let shiftPressed = event.modifierFlags.contains(.shift)
let keyS = event.keyCode == 1 // keyCode 1 代表 S 键
if commandPressed && shiftPressed && keyS {
print("Command + Shift + S 被触发!")
// 响应逻辑,比如触发截图等
}
}
优点:支持现代 Swift 和 SwiftUI 项目,集成方便,适合状态栏 App。
权限:需要开启“辅助权限”。
缺点:即使监听到事件,也无法「抢先处理」或「阻止冒泡」,因此无法防止其他 App 的快捷键被触发。
扩展阅读:《macOS全局监听事件NSEvent.addGlobalMonitorForEvents》。
2、注册全局快捷键RegisterEventHotKey
RegisterEventHotKey 是 macOS 的 Carbon API 中的一个函数,用于注册全局快捷键(热键),即使应用当前不在前台,也可以响应指定的键盘组合。
使用方法:
import Carbon.HIToolbox
var hotKeyRef: EventHotKeyRef? = nil
var gMyHotKeyID = EventHotKeyID(signature: OSType(bitPattern: UInt32(truncatingIfNeeded: "TEST".fourCharCodeValue)),
id: 1)
// 注册快捷键 cmd + shift + S
RegisterEventHotKey(UInt32(kVK_ANSI_S),
UInt32(cmdKey | shiftKey),
gMyHotKeyID,
GetEventDispatcherTarget(),
0,
&hotKeyRef)
extension String {
var fourCharCodeValue: UInt32 {
var result: UInt32 = 0
for character in self.utf8.prefix(4) {
result = (result << 8) + UInt32(character)
}
return result
}
}
优点:RegisterEventHotKey用于注册全局快捷键,在应用启动时,会监听应用层面的快捷键。支持阻止其他 App 的响应,允许注册多个快捷键(带有 ID)。
权限:不需要任何权限。
缺点:不能注册系统保留快捷键,RegisterEventHotKey 是 Carbon API,被认为是过时的API。
扩展阅读:《macOS注册全局快捷键RegisterEventHotKey》。
3、CGEventTap
CGEventTap(全称 Core Graphics Event Tap)是 macOS 提供的一种低层级机制,用于监听或拦截系统中的输入事件,如键盘按键、鼠标点击、滚轮滚动等,无论事件来自哪个 App 或窗口。
使用方法:
let mask: CGEventMask = (1 << CGEventType.keyDown.rawValue)
let eventTap = CGEvent.tapCreate(
tap: .cgSessionEventTap,
place: .headInsertEventTap,
options: .listenOnly, // 改为 .defaultTap 可拦截事件(会影响其他App),一般用 listenOnly
eventsOfInterest: mask,
callback: { _, type, event, _ in
print("监听到键盘被按下")
guard type == .keyDown else { return Unmanaged.passUnretained(event) }
// 获取按键
let keyCode = event.getIntegerValueField(.keyboardEventKeycode)
// 获取修饰键状态
let flags = event.flags
// Option + A 判断:A 是 keyCode 0(美式键盘),Option 是 .maskAlternate
if keyCode == 0 && flags.contains(.maskAlternate) {
print("检测到 Option + A - 调用截图方法")
DispatchQueue.main.async {
StatusBarController.shared.fullScreenshoot()
}
}
return Unmanaged.passUnretained(event)
},
userInfo: nil
)
guard let eventTap = eventTap else {
print("Failed to create event tap.")
return
}
let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
CGEvent.tapEnable(tap: eventTap, enable: true)
优点:可设置为 kCGHeadInsertEventTap 进行“事件劫持”(在系统处理前拦截),支持精细事件处理(鼠标滚轮、移动等)。
权限:需要启用“输入监控”权限。
缺点:使用复杂,在代码层面使用位运算符,这是因为Core Graphics(CG)是一套非常底层的框架,很多 API 都是从早期的 C 和 Objective-C 演变过来的。
扩展阅读:《macOS监听/拦截输入事件CGEventTap》。
4、底层输入IOHID
OHID 是 macOS(和 iOS)底层输入系统的一部分,属于 I/O Kit(驱动层框架),全称是:IOHID = I/O Human Interface Device。
使用方法:
let manager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
let matchingDict: [String: Any] = [
kIOHIDDeviceUsagePageKey as String: kHIDPage_GenericDesktop,
kIOHIDDeviceUsageKey as String: kHIDUsage_GD_Keyboard
]
IOHIDManagerSetDeviceMatching(manager!, matchingDict as CFDictionary)
let callback: IOHIDValueCallback = { context, result, sender, value in
let element = IOHIDValueGetElement(value)
let usagePage = IOHIDElementGetUsagePage(element)
let usage = IOHIDElementGetUsage(element)
let pressed = IOHIDValueGetIntegerValue(value) != 0
let this = Unmanaged<HIDKeyboardMonitor>.fromOpaque(context!).takeUnretainedValue()
let isOptionPressed = false
// Option 键的 usage 是 0x3A(左 Option)
if usage == 0x3A {
isOptionPressed = pressed
}
// A 键的 usage 是 0x04
if usage == 0x04 && pressed && isOptionPressed {
print("Option + A detected!")
}
}
IOHIDManagerRegisterInputValueCallback(manager!, callback, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()))
IOHIDManagerScheduleWithRunLoop(manager!, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
IOHIDManagerOpen(manager!, IOOptionBits(kIOHIDOptionsTypeNone))
优点:比 NSEvent / CGEvent 更低级,直接处理物理输入设备数据;适合需要极低延迟和细粒度控制的应用(如游戏或辅助输入设备);可拦截被系统或其他 App 吞掉的组合键;
权限:需要用户授权“输入监控”,关闭“App Sandbox”等重要权限!!!
缺点:使用复杂,需要匹配设备属性(Usage Page / Usage ID)。使用 C 接口,必须通过复杂的匹配字典、RunLoop 注册等;macOS 安全策略(如 TCC)不断增强,对 HID 接口做了沙盒限制;在 App Sandbox 中几乎完全无法使用。
扩展阅读:《macOS底层输入IOHID》。
总结
1、仅监听快捷键,不影响其他 App:NSEvent.addGlobalMonitorForEvents。
2、注册真正的快捷键(可阻止其他 App 响应):RegisterEventHotKey。
3、拦截所有输入事件(如阻止系统行为):CGEventTap(需输入监控权限)。
4、监听极低层设备事件(如自定义键盘):IOHIDManager(需关闭沙盒 + 高权限),在Swift项目中,未能通过IOHIDManager实现快捷键的监听功能,这个方法的可用性差。
对于普通的应用快捷键,推荐使用NSEvent.addGlobalMonitorForEvents,因为它的用法支持现代Swift和SwiftUI项目,集成方便。
相关文章
1、macOS全局监听事件NSEvent.addGlobalMonitorForEvents:https://fangjunyu.com/2025/07/27/macos%e5%85%a8%e5%b1%80%e7%9b%91%e5%90%ac%e4%ba%8b%e4%bb%b6nsevent-addglobalmonitorforevents/
2、macOS注册全局快捷键RegisterEventHotKey:https://fangjunyu.com/2025/07/26/macos%e6%b3%a8%e5%86%8c%e5%85%a8%e5%b1%80%e5%bf%ab%e6%8d%b7%e9%94%aeregistereventhotkey/
3、macOS监听/拦截输入事件CGEventTap:https://fangjunyu.com/2025/07/23/macos%e7%9b%91%e5%90%ac-%e6%8b%a6%e6%88%aa%e8%be%93%e5%85%a5%e4%ba%8b%e4%bb%b6cgeventtap/
4、macOS底层输入IOHID:https://fangjunyu.com/2025/07/27/macos%e5%ba%95%e5%b1%82%e8%be%93%e5%85%a5iohid/