
最近想做个 Apple Note 的强化工具,市面上虽然有了,但是还是想着自己去实践一下,毕竟有的是软件而不是知识。纸上得来终觉浅。想要实现的功能第一版其实也很简单,就是支持 “/” 唤起一个上面的菜单栏,和 Notion 有点类似。
其实原理很简单,就是监听键盘输入,获取当前输入的光标位置,然后显示一个浮动的菜单,所以分三步:
- 监听键盘输入
- 获取当前输入的光标位置
- 显示浮动菜单
其中最后两个步骤不在本次讨论范围内,所以重点在于如何去监听键盘事件。
NSEvent.addGlobalMonitorForEvents
定义如下:
Installs an event monitor that receives copies of events the system posts to other applications.
函数定义:
class func addGlobalMonitorForEvents(
matching mask: [NSEvent](https://developer.apple.com/documentation/appkit/nsevent).[EventTypeMask](https://developer.apple.com/documentation/appkit/nsevent/eventtypemask),
handler block: @escaping ([NSEvent](https://developer.apple.com/documentation/appkit/nsevent)) -> [Void](https://developer.apple.com/documentation/Swift/Void)
) -> Any?
所以就有如下的代码


但是使用 NSEvent.addGlobalMonitorForEvents 有个问题 — 它不能阻止原本的键盘事件向上冒泡。这带来直接影响就是,当菜单出现之后,上下移动光标的时候,光标在菜单移动的同时也会改变原本在 Apple Note 里光标的位置。这也是官方文档提到的:
Events are delivered asynchronously to your app and you can only observe the event; you cannot modify or otherwise prevent the event from being delivered to its original target application.
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted()).
Note that your handler will not be called for events that are sent to your own application.
所以 NSEvent.addGlobalMonitorForEvents 不适合当前的场景,所以就找到了 CGEventTapCreate
CGEventTapCreate

它可以可以真正的实现系统级别拦截,在它们被发布到应用程序的事件队列之前,监听(甚至修改或丢弃)事件。所以就有如下的代码


需要注意的是,CGEventTapCreate 需要特殊的用户权限,对应的应用程序必须被用户添加到“系统偏好设置” -> “安全性与隐私” -> “隐私” -> “辅助功能”列表中。否则 CGEventTapCreate 会失败。
参考链接
tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) | Apple Developer Documentation
addGlobalMonitorForEvents(matching:handler:) | Apple Developer Documentation
最近想做个 Apple Note 的强化工具,市面上虽然有了,但是还是想着自己去实践一下,毕竟有的是软件而不是知识。纸上得来终觉浅。想要实现的功能第一版其实也很简单,就是支持 “/” 唤起一个上面的菜单栏,和 Notion 有点类似。
其实原理很简单,就是监听键盘输入,获取当前输入的光标位置,然后显示一个浮动的菜单,所以分三步:
其中最后两个步骤不在本次讨论范围内,所以重点在于如何去监听键盘事件。
NSEvent.addGlobalMonitorForEvents
定义如下:
函数定义:
所以就有如下的代码


但是使用 NSEvent.addGlobalMonitorForEvents 有个问题 — 它不能阻止原本的键盘事件向上冒泡。这带来直接影响就是,当菜单出现之后,上下移动光标的时候,光标在菜单移动的同时也会改变原本在 Apple Note 里光标的位置。这也是官方文档提到的:
所以 NSEvent.addGlobalMonitorForEvents 不适合当前的场景,所以就找到了 CGEventTapCreate
CGEventTapCreate

它可以可以真正的实现系统级别拦截,在它们被发布到应用程序的事件队列之前,监听(甚至修改或丢弃)事件。所以就有如下的代码


需要注意的是,CGEventTapCreate 需要特殊的用户权限,对应的应用程序必须被用户添加到“系统偏好设置” -> “安全性与隐私” -> “隐私” -> “辅助功能”列表中。否则 CGEventTapCreate 会失败。
参考链接
tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) | Apple Developer Documentation
addGlobalMonitorForEvents(matching:handler:) | Apple Developer Documentation