Я пытаюсь иметь обработчик в своем приложении Mac OS X, написанном в Swift, для глобальной (общесистемной) комбинации горячих клавиш, но я просто не могу найти для этого подходящую документацию. Я читал, что мне нужно будет возиться в каком-то унаследованном Carbon API, нет ли лучшего способа? Можете ли вы показать мне некоторые доказательства концепции Swift-кода? Спасибо заранее!
Как слушать глобальные горячие клавиши с Swift в приложении OS X?
Ответ 1
Начиная с 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)
Ответ 2
Я не верю, что вы можете сделать это сегодня в 100% Swift. Вам нужно вызвать InstallEventHandler()
или CGEventTapCreate()
, и для обоих из них требуется CFunctionPointer
, который не может быть создан в Swift. Ваш лучший план - использовать установленные решения ObjC, такие как DDHotKey и перейти к Swift.
Вы можете попробовать использовать NSEvent.addGlobalMonitorForEventsMatchingMask(handler:)
, но это делает только копии событий. Вы не можете их использовать. Это означает, что горячая клавиша также будет передана в текущее активное приложение, что может вызвать проблемы. Вот пример, но я рекомендую подход 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:)
, чтобы добавить локальный обработчик.
Ответ 3
Быстрое обновление 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)
Ответ 4
Взгляните на библиотеку HotKey. Вы можете просто использовать Карфаген, чтобы внедрить его в свое приложение. Библиотека HotKey
Ответ 5
есть довольно хакерский, но довольно простой обходной путь, если у вашего приложения есть Menu
:
- добавить новый
MenuItem
(возможно, назвать его что-то вроде "Dummy for Hotkey") - в инспекторе атрибутов введите удобную горячую клавишу в поле "
Key Equivalent
- установите для параметра
Allowed when Hidden
,Enabled
иHidden
значение true - свяжите его с IBAction, чтобы сделать то, что должна делать ваша горячая клавиша
сделанный!
Ответ 6
Следующий код работает у меня для Swift 5.0.1. Это решение представляет собой сочетание решения из принятого ответа Чарли Монро и рекомендации Роба Нейпира использовать DDHotKey.
DDHotKey, кажется, работает из коробки, но у него было одно ограничение, которое мне пришлось изменение: eventKind жестко запрограммирован на kEventHotKeyReleased
, в то время как мне были нужны оба типа событий: kEventHotKeyPressed
и kEventHotKeyReleased
.
eventSpec.eventKind = kEventHotKeyReleased;
Если вы хотите обрабатывать события Pressed и Released, просто добавьте второй вызов InstallEventHandler
, который регистрирует другой тип события.
Это полный пример кода, который регистрирует клавишу "Command + R" для типа kEventHotKeyReleased
.
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)
}
}