ich dieses Problem selbst tatsächlich gelöst nur den anderen Tag. Ich schrieb eine blog post darauf, sowie eine Gist
Ich werde den Blog-Post und endgültigen Code einbetten, nur für den Fall, dass der Blog oder Gist jemals weggehen. Hinweis: Dies ist ein sehr langer Beitrag, der ausführlich auf die Klasse in erstellt und was Sie tun können, um andere Methoden in der Delegate Ihrer App aufrufen. Wenn Sie nur das fertige Produkt (die MediaApplication-Klasse) möchten, gehen Sie nach unten. Es befindet sich direkt über den XML- und Info.plist-Informationen.
Für den Anfang, die wichtigsten Ereignisse aus den Medien Schlüssel bekommen Sie eine Klasse erstellen müssen, die NSApplication
erstreckt. Dies ist so einfach wie
import Cocoa
class MediaApplication: NSApplication {
}
Als nächstes müssen wir die sendEvent()
Funktion
override func sendEvent(event: NSEvent) {
if (event.type == .SystemDefined && event.subtype.rawValue == 8) {
let keyCode = ((event.data1 & 0xFFFF0000) >> 16)
let keyFlags = (event.data1 & 0x0000FFFF)
// Get the key state. 0xA is KeyDown, OxB is KeyUp
let keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA
let keyRepeat = (keyFlags & 0x1)
mediaKeyEvent(Int32(keyCode), state: keyState, keyRepeat: Bool(keyRepeat))
}
super.sendEvent(event)
}
nun außer Kraft zu setzen, ich behaupte nicht ganz zu verstehen, was hier vor sich geht, aber ich glaube, ich habe ein anständige Idee. NSEvent
Objekte enthalten mehrere Schlüsseleigenschaften: type
, subtype
, data1
und data2
. Type
und subtype
sind ziemlich selbsterklärend, aber data1
und data2
sind extrem vage. Da der Code nur data1
verwendet, sehen wir uns das an. Soweit ich das beurteilen kann, enthält data1
alle Daten, die ein Schlüsselereignis umgeben. Das bedeutet, dass es den Schlüsselcode und alle Schlüsselflags enthält. Es scheint, dass Schlüssel-Flags Informationen über den Status des Schlüssels enthalten (Ist der Schlüssel gedrückt? Wurde der Schlüssel freigegeben?), Sowie, ob der Schlüssel gedrückt gehalten wird oder nicht und das Signal wiederholt wird. Ich nehme auch an, dass der Schlüsselcode und die Schlüsselflaggen beide die Hälfte der Daten belegen, die in data1
enthalten sind, und die bitweisen Operationen trennen diese Daten in geeignete Variablen. Nachdem wir die Werte erhalten haben, die wir brauchen, rufen wir mediaKeyEvent()
an, auf die ich gleich eingehen werde. Unabhängig davon, welche Ereignisse an unsere MediaApplication
gesendet werden, möchten wir, dass die Standardeinstellung NSApplication
auch alle Ereignisse behandelt. Rufen Sie dazu am Ende der Funktion super.sendEvent(event)
auf. Schauen wir uns nun mediaKeyEvent()
an.
func mediaKeyEvent(key: Int32, state: Bool, keyRepeat: Bool) {
// Only send events on KeyDown. Without this check, these events will happen twice
if (state) {
switch(key) {
case NX_KEYTYPE_PLAY:
// Do work
break
case NX_KEYTYPE_FAST:
// Do work
break
case NX_KEYTYPE_REWIND:
// Do work
break
default:
break
}
}
}
Hier fängt es an, Spaß zu machen. Zuerst einmal wollen wir nur prüfen, welche Taste gedrückt wird, wenn state
wahr ist, was in diesem Fall immer dann geschieht, wenn die Taste gedrückt wird. Sobald wir die Schlüssel überprüfen, suchen wir nach NX_KEYTYPE_PLAY
, NX_KEYTYPE_FAST
und NX_KEYTYPE_REWIND
. Wenn ihre Funktionen nicht offensichtlich sind, ist NX_KEYTYPE_PLAY
die Wiedergabe/Pause-Taste, NX_KEYTYPE_FAST
ist die nächste Taste, und NX_KEYTYPE_REWIND
ist die vorherige Taste. Im Moment passiert nichts, wenn eine dieser Tasten gedrückt wird, also lassen Sie uns eine mögliche Logik durchgehen. Wir beginnen mit einem einfachen Szenario.
case NX_KEYTYPE_PLAY:
print("Play")
break
Mit diesem Code an Ort und Stelle, wenn Ihre Anwendung erkennt, dass die Wiedergabe/Pause-Taste, die Sie mit der Konsole „Play“ ausgedruckt werden sehen, gedrückt wurde. Einfach, oder? Lassen Sie uns den Ante durch Aufruf von Funktionen in Ihrer Anwendung NSApplicationDelegate
. Zuerst nehmen wir an, dass Ihre NSApplicationDelegate
eine Funktion namens printMessage
hat. Wir werden es ändern, wie wir gehen, also achten Sie genau auf die Änderungen. Sie werden geringfügig sein, aber die Änderungen werden sich darauf auswirken, wie Sie sie von mediaEventKey
anrufen.
func printMessage() {
print("Hello World")
}
Dies ist der einfachste Fall. Wenn printMessage()
aufgerufen wird, sehen Sie "Hello World" in Ihrer Konsole. Sie können dies anrufen, indem Sie performSelector
auf Ihrem NSApplicationDelegate
aufrufen, der über die MediaApplication
erreichbar ist. performSelector
nimmt eine Selector
das ist nur der Name der Funktion in Ihrem NSApplicationDelegate
.
case NX_KEYTYPE_PLAY:
delegate!.performSelector("printMessage")
break
Nun, wenn Ihre Anwendung erkennt, dass die Wiedergabe/Pause-Taste gedrückt wurde, werden Sie „Hallo Welt“ gedruckt auf der Konsole sehen. Lassen Sie uns die Dinge mit einer neuen Version von printMessage
, die einen Parameter aufnimmt, um eine Stufe höher legen.
func printMessage(arg: String) {
print(arg)
}
Die Idee ist nun, dass, wenn printMessage("Hello World")
genannt wird, werden Sie „Hallo Welt“ in der Konsole sehen. Wir können jetzt den performSelector
Aufruf ändern, um die Übergabe eines Parameters zu behandeln.
case NX_KEYTYPE_PLAY:
delegate!.performSelector("printMessage:", withObject: "Hello World")
break
Es gibt einige Dinge, die Sie über diese Änderung beachten sollten. Zuerst ist es wichtig, die :
zu bemerken, die zu der Selector
hinzugefügt wurde. Dies trennt den Funktionsnamen von dem Parameter, wenn er an den Delegaten gesendet wird. Wie es funktioniert, ist nicht zu wichtig, um sich daran zu erinnern, aber es ist etwas in Richtung des Delegierten, der printMessage:"Hello World"
aufruft. Ich bin mir ziemlich sicher, dass das nicht zu 100% korrekt ist, da es wahrscheinlich eine Objekt-ID irgendeiner Art verwenden würde, aber ich habe noch nicht ausführlich auf die Einzelheiten eingegangen. In jedem Fall ist die wichtige Sache zu erinnern, :
hinzuzufügen, wenn Sie einen Parameter übergeben. Die zweite Sache zu beachten ist, dass wir einen withObject
Parameter hinzugefügt haben. withObject
nimmt einen AnyObject?
als Wert. In diesem Fall übergeben wir einfach eine String
, denn das ist, was sucht. Wenn Ihre Anwendung feststellt, dass die Wiedergabe/Pause-Taste gedrückt wurde, sollten Sie in der Konsole immer noch "Hello World" sehen. Sehen wir uns einen letzten Anwendungsfall an: eine Version von printMessage
, die nicht einen, sondern zwei Parameter enthält.
func printMessage(arg: String, _ arg2: String) {
print(arg)
}
Nun, wenn printMessage("Hello", "World")
genannt wird, werden Sie "Hallo Welt" in der Konsole sehen. Wir können jetzt den performSelector
Aufruf ändern, um die Übergabe von zwei Parametern zu behandeln.
case NX_KEYTYPE_PLAY:
delegate!.performSelector("printMessage::", withObject: "Hello", withObject: "World")
break
Wie zuvor, gibt es zwei Dinge, die hier zu beachten sind. Zuerst fügen wir nun zwei :
zum Ende der Selector
hinzu. Wie zuvor ist dies so, dass der Delegierte Informationen weiterleiten kann, die die Parameter enthalten. Auf einer sehr grundlegenden Ebene würde es etwa wie printMessage:"Hello":"World"
aussehen, aber ich weiß nicht, wie es wirklich auf einer tieferen Ebene aussieht. Die zweite Sache zu bemerken ist, dass wir einen zweiten withObject
Parameter zum performSelector
Anruf hinzugefügt haben. Wie zuvor nimmt diese withObject
eine AnyObject?
als Wert und wir sind in String
übergeben, weil das printMessage
will.Wenn Ihre Anwendung feststellt, dass die Wiedergabe/Pause-Taste gedrückt wurde, sollten Sie in der Konsole immer noch "Hello World" sehen.
Eine letzte Sache zu beachten ist, dass performSelector
nur bis zu zwei Parameter akzeptieren kann. Ich würde es sehr gerne sehen, dass Swift Konzepte wie Splatting oder Varargs hinzufügt, so dass diese Einschränkung schließlich verschwindet, aber bis jetzt nur vermeiden, Funktionen aufzurufen, die mehr als zwei Parameter erfordern.
Dies ist, was eine sehr einfache MediaApplication
Klasse, druckt nur einiger Text aussehen würde, wenn Sie mit allem, was oben fertig sind:
import Cocoa
class MediaApplication: NSApplication {
override func sendEvent(event: NSEvent) {
if (event.type == .SystemDefined && event.subtype.rawValue == 8) {
let keyCode = ((event.data1 & 0xFFFF0000) >> 16)
let keyFlags = (event.data1 & 0x0000FFFF)
// Get the key state. 0xA is KeyDown, OxB is KeyUp
let keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA
let keyRepeat = (keyFlags & 0x1)
mediaKeyEvent(Int32(keyCode), state: keyState, keyRepeat: Bool(keyRepeat))
}
super.sendEvent(event)
}
func mediaKeyEvent(key: Int32, state: Bool, keyRepeat: Bool) {
// Only send events on KeyDown. Without this check, these events will happen twice
if (state) {
switch(key) {
case NX_KEYTYPE_PLAY:
print("Play")
break
case NX_KEYTYPE_FAST:
print("Next")
break
case NX_KEYTYPE_REWIND:
print("Prev")
break
default:
break
}
}
}
}
Nun soll ich auch hinzufügen, dass standardmäßig Ihre Anwendung Verwenden Sie den Standard NSApplication
, wenn es ausgeführt wird. Wenn Sie die MediaApplication
verwenden möchten, die dieser ganze Beitrag geht, müssen Sie voran gehen und die Info.plist
Datei Ihrer Anwendung ändern. Wenn Sie in der grafischen Ansicht sind, dann wird es in etwa so aussehen:
Ansonsten wird es in etwa so aussehen:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015 Chris Rees. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
In jedem Fall werden Sie ändern möchten die Eigenschaft. Der neue Wert enthält den Namen Ihres Projekts, also wird es etwa so aussehen: Notify.MediaApplication
. Sobald Sie die Änderung vorgenommen haben, führen Sie Ihre Anwendung aus und verwenden Sie diese Medienschlüssel!
hey @Serneum danke für nette Details. Hier ist mein Problem: können wir dieses Ereignis für iTunes blockieren? Wenn Sie die Taste drücken, starten Sie iTunes. – AJit
Um den Start von iTunes auf dem Media Key zu übernehmen, drücken Sie. https://github.com/nevyn/SPMediaKeyTap – AJit
Schöne Lösung, aber es funktioniert nicht, wenn Sie die Anwendung programmgesteuert erstellen (mit main.swift anstelle von Storyboard oder XIB-Datei) – ErwinGO