macOS全局监听事件NSEvent.addGlobalMonitorForEvents
macOS全局监听事件NSEvent.addGlobalMonitorForEvents

macOS全局监听事件NSEvent.addGlobalMonitorForEvents

NSEvent.addGlobalMonitorForEvents(matching:handler:) 可以用来全局监听鼠标或键盘事件,即使 App 不在前台,也能收到这些事件。要全局监听快捷键,需要监听键盘事件(如 .keyDown),然后判断是否按下了特定组合键。

权限要求

要监听全局事件,必须开启“辅助功能” 权限:

在启动App后,建议使用AXIsProcessTrustedWithOptions主动检测“辅助功能” 权限,如果没有开启,则手动配置提示弹窗:

import ApplicationServices

func checkAccessibilityPermission() -> Bool {
    let options = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: true] as CFDictionary
    return AXIsProcessTrustedWithOptions(options)
}

基本用法

// 添加全局监听
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 被触发!")
        // 响应逻辑,比如触发截图等
    }
}

代码解析

1、添加全局监听器

NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { [weak self] event in
    self?.handleGlobalKeyEvent(event)
}

1)NSEvent.addGlobalMonitorForEvents(…):macOS 提供的 API,用来全局监听键盘/鼠标事件,即使应用不在前台也能收到事件。

2).keyDown:表示监听的是“按下键盘”的事件(还有 .keyUp、.mouseMoved 等),属于NSEvent.EventType枚举类型,更多类型请见《macOS用户输入事件NSEvent》。

3){ [weak self] event in … }:事件触发后要执行的代码。用 [weak self] 防止循环引用,避免 self 被强引用导致内存泄漏。

4)event:是 NSEvent 类型,表示此次按下的键的信息,比如 keyCode、modifierFlags(修饰键)、字符等。

2、处理事件逻辑

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 被触发!")
        // 响应逻辑,比如触发截图等
    }
}

1)modifierFlags.contains(.command):判断此次按键是否同时按下了 Command 键。

2)modifierFlags.contains(.shift):判断是否按下了 Shift 键。

3)event.keyCode == 1:判断按下的键是否是 S(keyCode 1 代表字母 S)。

4)合并条件判断:只有当同时按下 Command + Shift + S 才执行后续逻辑。

注意:keyCode 是键盘的物理位置编号,不同于字符(event.characters)。它跨语言更可靠。

3、移除监听器

在实际使用中,还会涉及移除监听器,这里需要将NSEvent.addGlobalMonitorForEvents保存到一个类的变量,以便强引用NSEvent监听器:

var monitor: Any?
monitor = NSEvent.addGlobalMonitorForEvents...

当需要移除监听器时,使用removeMonitor方法:

if let monitor = monitor {
    NSEvent.removeMonitor(monitor)
}

1)monitor:是 addGlobalMonitorForEvents 返回的监听器对象。

2)NSEvent.removeMonitor(…):移除这个监听器,防止内存泄漏或监听过多事件导致资源浪费。

3)在macOS中,通常在AppDelegate的applicationWillTerminate退出应用阶段或不再需要监听时调用。

键码和修饰符

1、macOS 的 NSEvent.keyCode 返回的是“键码”,不是字符。比如:

event.keyCode == 0时,代表A键,1表示S键,2表示D键…

可以用以下方式临时打印 keyCode:

print("keyCode: \(event.keyCode)")

AppleScript键码图片(图片来源eastmanreference):

2、modifiers: 组合修饰键(数组):.command、.control、.option、.shift:

let optionPressed = event.modifierFlags.contains(.option)
let controlPressed = event.modifierFlags.contains(.control)

要监听多个快捷键,可以写成:

if commandPressed && shiftPressed {
    switch event.keyCode {
    case 1:
        print("S")
    case 3:
        print("F")
    default:
        break
    }
}

总结

addGlobalMonitorForEvents 无法阻止事件传播,它只是“监听”;如果希望阻止快捷键传递到系统或其他 App,需要使用 addLocalMonitorForEvents(但只能用于前台 App)。

相关文章

1、macOS用户输入事件NSEvent:https://fangjunyu.com/2025/07/04/macos%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e4%ba%8b%e4%bb%b6nsevent/

2、SwiftUI使用NSEvent监听剪贴板:https://fangjunyu.com/2025/07/22/swiftui%e4%bd%bf%e7%94%a8nsevent%e7%9b%91%e5%90%ac%e5%89%aa%e8%b4%b4%e6%9d%bf/

3、Complete list of AppleScript key codes:https://eastmanreference.com/complete-list-of-applescript-key-codes

完整代码

import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    var monitor: Any?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // 添加全局监听
        monitor = NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { [weak self] event in
            self?.handleGlobalKeyEvent(event)
        }
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // 清除监听器
        if let monitor = monitor {
            NSEvent.removeMonitor(monitor)
        }
    }

    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 被触发!")
            // 响应逻辑,比如触发截图等
        }
    }
}
   

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

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

发表回复

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