Cocoa 如何在OS X应用程序中使用Swift收听全球热键?
我试图在我的MacOSX应用程序中使用Swift编写一个全局(系统范围)热键组合的处理程序,但我就是找不到合适的文档。我已经读到,我将不得不在一些遗留的碳API中乱搞,没有更好的办法吗?你能给我看一些概念验证Swift代码吗?提前谢谢 我不相信你今天能百分之百地做到这一点。您需要调用Cocoa 如何在OS X应用程序中使用Swift收听全球热键?,cocoa,swift,Cocoa,Swift,我试图在我的MacOSX应用程序中使用Swift编写一个全局(系统范围)热键组合的处理程序,但我就是找不到合适的文档。我已经读到,我将不得不在一些遗留的碳API中乱搞,没有更好的办法吗?你能给我看一些概念验证Swift代码吗?提前谢谢 我不相信你今天能百分之百地做到这一点。您需要调用InstallEventHandler()或CGEventTapCreate(),这两者都需要CFunctionPointer,而不能在Swift中创建。您的最佳计划是使用已建立的ObjC解决方案,如和Swift桥接
InstallEventHandler()
或CGEventTapCreate()
,这两者都需要CFunctionPointer
,而不能在Swift中创建。您的最佳计划是使用已建立的ObjC解决方案,如和Swift桥接
您可以尝试使用NSEvent.addGlobalMonitorForEventsMatchingMask(处理程序:)
,但这只能复制事件。你不能吃掉它们。这意味着热键也将传递给当前活动的应用程序,这可能会导致问题。这里有一个例子,但我推荐ObjC方法;这几乎肯定会更好
let keycode = UInt16(kVK_ANSI_X)
let keymask: NSEventModifierFlags = .CommandKeyMask | .AlternateKeyMask | .ControlKeyMask
func handler(event: NSEvent!) {
if event.keyCode == self.keycode &&
event.modifierFlags & self.keymask == self.keymask {
println("PRESSED")
}
}
// ... to set it up ...
let options = NSDictionary(object: kCFBooleanTrue, forKey: kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString) as CFDictionaryRef
let trusted = AXIsProcessTrustedWithOptions(options)
if (trusted) {
NSEvent.addGlobalMonitorForEventsMatchingMask(.KeyDownMask, handler: self.handler)
}
这还要求此应用程序的可访问性服务获得批准。它也不会捕获发送到您自己的应用程序的事件,因此您必须使用响应程序链捕获它们,我们使用
addLocalMointorForEventsMatchingMask(handler:)
添加本地处理程序。自Swift 2.0以来,您现在可以向C API传递函数指针
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.signature = OSType("swat".fourCharCodeValue)
gMyHotKeyID.id = UInt32(keyCode)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyPressed)
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {(nextHanlder, theEvent, userData) -> OSStatus in
var hkCom = EventHotKeyID()
GetEventParameter(theEvent, EventParamName(kEventParamDirectObject), EventParamType(typeEventHotKeyID), nil, sizeof(EventHotKeyID), nil, &hkCom)
// Check that hkCom in indeed your hotkey ID and handle it.
}, 1, &eventType, nil, nil)
// Register hotkey.
let status = RegisterEventHotKey(UInt32(keyCode), UInt32(modifierKeys), gMyHotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef)
设置的快速Swift 3更新:
let opts = NSDictionary(object: kCFBooleanTrue, forKey: kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString) as CFDictionary
guard AXIsProcessTrustedWithOptions(opts) == true else { return }
NSEvent.addGlobalMonitorForEvents(matching: .keyDown, handler: self.handler)
看看热键库。您可以简单地使用迦太基将其实现到您自己的应用程序中。
如果你的应用程序有一个
菜单,有一个非常简单的解决方法:
- 添加一个新的
MenuItem
(可以称之为“热键的虚拟”)
- 在属性检查器中,方便地在
等效键
字段中输入热键
- 将隐藏时允许的
、启用的和隐藏时的设置为true
- 将其与IBAction链接,以执行热键应该执行的任何操作
完成了 以下代码适用于我的Swift 5.0.1。该解决方案结合了Charlie Monroe接受的答案和Rob Napier使用DDHotKey的建议
DDHotKey似乎是开箱即用的,但它有一个我必须更改的限制:eventKind硬编码为kEventHotKeyReleased
,而我需要同时按下kEventHotKeyPressed
和kEventHotKeyReleased
事件类型
eventSpec.eventKind = kEventHotKeyReleased;
如果要同时处理已按下和已释放的事件,只需添加第二个InstallEventHandler
调用,该调用将注册其他类型的事件
这是为kEventHotKeyReleased
类型注册“Command+R”键的完整代码示例
import Carbon
extension String {
/// This converts string to UInt as a fourCharCode
public var fourCharCodeValue: Int {
var result: Int = 0
if let data = self.data(using: String.Encoding.macOSRoman) {
data.withUnsafeBytes({ (rawBytes) in
let bytes = rawBytes.bindMemory(to: UInt8.self)
for i in 0 ..< data.count {
result = result << 8 + Int(bytes[i])
}
})
}
return result
}
}
class HotkeySolution {
static
func getCarbonFlagsFromCocoaFlags(cocoaFlags: NSEvent.ModifierFlags) -> UInt32 {
let flags = cocoaFlags.rawValue
var newFlags: Int = 0
if ((flags & NSEvent.ModifierFlags.control.rawValue) > 0) {
newFlags |= controlKey
}
if ((flags & NSEvent.ModifierFlags.command.rawValue) > 0) {
newFlags |= cmdKey
}
if ((flags & NSEvent.ModifierFlags.shift.rawValue) > 0) {
newFlags |= shiftKey;
}
if ((flags & NSEvent.ModifierFlags.option.rawValue) > 0) {
newFlags |= optionKey
}
if ((flags & NSEvent.ModifierFlags.capsLock.rawValue) > 0) {
newFlags |= alphaLock
}
return UInt32(newFlags);
}
static func register() {
var hotKeyRef: EventHotKeyRef?
let modifierFlags: UInt32 =
getCarbonFlagsFromCocoaFlags(cocoaFlags: NSEvent.ModifierFlags.command)
let keyCode = kVK_ANSI_R
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.id = UInt32(keyCode)
// Not sure what "swat" vs "htk1" do.
gMyHotKeyID.signature = OSType("swat".fourCharCodeValue)
// gMyHotKeyID.signature = OSType("htk1".fourCharCodeValue)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyReleased)
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {
(nextHanlder, theEvent, userData) -> OSStatus in
// var hkCom = EventHotKeyID()
// GetEventParameter(theEvent,
// EventParamName(kEventParamDirectObject),
// EventParamType(typeEventHotKeyID),
// nil,
// MemoryLayout<EventHotKeyID>.size,
// nil,
// &hkCom)
NSLog("Command + R Released!")
return noErr
/// Check that hkCom in indeed your hotkey ID and handle it.
}, 1, &eventType, nil, nil)
// Register hotkey.
let status = RegisterEventHotKey(UInt32(keyCode),
modifierFlags,
gMyHotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef)
assert(status == noErr)
}
}
进口碳
扩展字符串{
///这将字符串转换为UInt作为fourCharCode
公共变量fourCharCodeValue:Int{
变量结果:Int=0
如果let data=self.data(使用:String.Encoding.macOSRoman){
data.withUnsafeBytes({(rawBytes)在
let bytes=rawBytes.bindMemory(to:UInt8.self)
对于0中的i..0){
newFlags |=控制键
}
if((flags&NSEvent.ModifierFlags.command.rawValue)>0){
newFlags |=cmdKey
}
if((flags&NSEvent.ModifierFlags.shift.rawValue)>0){
newFlags |=移位键;
}
if((flags&NSEvent.ModifierFlags.option.rawValue)>0){
newFlags |=选项键
}
if((flags&NSEvent.ModifierFlags.capsLock.rawValue)>0){
newFlags |=字母锁
}
返回UInt32(新标志);
}
静态func寄存器(){
var hotKeyRef:EventHotKeyRef?
让修饰符标记:UInt32=
GetCarbonFlagsFromCoaFlags(cocoaFlags:NSEvent.ModifierFlags.command)
设keyCode=kVK\u ANSI\u R
var gMyHotKeyID=EventHotKeyID()
gMyHotKeyID.id=UInt32(keyCode)
//不知道“swat”和“htk1”做什么。
gMyHotKeyID.signature=OSType(“swat”.fourCharCodeValue)
//gMyHotKeyID.signature=OSType(“htk1”.fourCharCodeValue)
var eventType=EventTypeSpec()
eventType.eventClass=OSType(kEventClassKeyboard)
eventType.eventKind=OSType(kEventHotKeyReleased)
//安装处理程序。
InstallEventHandler(GetApplicationEventTarget(){
(下一个文件夹、事件、用户数据)->OSStatus in
//var hkCom=EventHotKeyID()
//GetEventParameter(事件,
//EventParamName(kEventParamDirectObject),
//EventParamType(类型EventHotKeyId),
//零,
//MemoryLayout.size,
//零,
//(香港电讯)
NSLog(“命令+R已发布!”)
返回noErr
///检查hkCom是否确实存在您的热键ID并处理它。
},1,&事件类型,无,无)
//注册热键。
let status=注册EventThotKey(UInt32(keyCode),
修饰符标记,
gMyHotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef)
断言(状态==noErr)
}
}
您到底试过什么?热键是什么意思?您试过NSEvent global monitor或CGEventTap吗注:碳基热键API不需要访问权限(不能在Mac App Store上),虽然这确实更容易实现。有一些应用程序,如CopyLess 2,可以监听按键,不需要辅助功能,你知道吗?也许商店里的应用程序不需要它?在Swift 4
中不起作用,如果没有更详细的解释,就不可能实现。在Swift 4中效果很好,我知道转换我的项目