2015-09-10 6 views

Antwort

13

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:

Info.plist

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!

+0

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

+0

Um den Start von iTunes auf dem Media Key zu übernehmen, drücken Sie. https://github.com/nevyn/SPMediaKeyTap – AJit

+0

Schöne Lösung, aber es funktioniert nicht, wenn Sie die Anwendung programmgesteuert erstellen (mit main.swift anstelle von Storyboard oder XIB-Datei) – ErwinGO

Verwandte Themen