macOS监听快捷键的几种方法
macOS监听快捷键的几种方法

macOS监听快捷键的几种方法

在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/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注