在Swift中捕获OSX媒体控制按钮

我希望我的应用程序响应 F7 , F8 和 F9 键盘媒体控制按钮. 

我知道这个可爱的库,但是它不能与Swift结合使用: https://github.com /nevyn/SPMediaKeyTap 

我实际上是在前几天解决了这个问题.我写了一个 blog post,还有一个 Gist

我将嵌入博客文章和最终代码,以防万一博客或Gist消失.注意:这是一篇非常长的帖子,详细介绍了如何构建类以及如何在App的委托中调用其他方法.如果您想要的只是成品(MediaApplication类),请前往底部.它位于XML和Info.plist信息之上.

首先,要从媒体键获取关键事件,您需要创建一个扩展NSApplication的类.这很简单

 
  1. import Cocoa
  2. class MediaApplication: NSApplication {
  3. }

接下来,我们需要覆盖sendEvent()函数

 
  1. override func sendEvent(event: NSEvent) {
  2. if (event.type == .SystemDefined && event.subtype.rawValue == 8) {
  3. let keyCode = ((event.data1 & 0xFFFF0000) >> 16)
  4. let keyFlags = (event.data1 & 0x0000FFFF)
  5. // Get the key state. 0xA is KeyDown,OxB is KeyUp
  6. let keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA
  7. let keyRepeat = (keyFlags & 0x1)
  8. mediaKeyEvent(Int32(keyCode),state: keyState,keyRepeat: Bool(keyRepeat))
  9. }
  10. super.sendEvent(event)
  11. }

现在,我并没有假装完全理解这里发生了什么,但我认为我有一个不错的主意. NSEvent对象包含几个关键属性:type,subtype,data1和data2.类型和子类型是相当不言自明的,但data1和data2非常模糊.由于代码只使用data1,这就是我们要看的内容.据我所知,data1包含关键事件周围的所有数据.这意味着它包含密钥代码和任何密钥标志.似乎键标志包含有关键的状态的信息(是否按下了键?键是否被释放?)以及键是否被按下并重复信号.我还猜测密钥代码和密钥标记都占用了data1中包含的一半数据,而按位操作将数据分成适当的变量.在得到我们需要的值之后,我们调用mediaKeyEvent(),我将在稍后介绍.无论发送到我们的MediaApplication的事件是什么,我们都希望认的NSApplication也能处理所有事件.为此,我们在函数末尾调用super.sendEvent(event).现在,我们来看看mediaKeyEvent().

 
  1. func mediaKeyEvent(key: Int32,state: Bool,keyRepeat: Bool) {
  2. // Only send events on KeyDown. Without this check,these events will happen twice
  3. if (state) {
  4. switch(key) {
  5. case NX_KEYTYPE_PLAY:
  6. // Do work
  7. break
  8. case NX_KEYTYPE_FAST:
  9. // Do work
  10. break
  11. case NX_KEYTYPE_REWIND:
  12. // Do work
  13. break
  14. default:
  15. break
  16. }
  17. }
  18. }

事情开始变得有趣.首先,我们只想检查当状态为真时按下了什么键,在这种情况下,只要按下键就可以了.一旦我们检查了密钥,我们就会查找NX_KEYTYPE_PLAY,NX_KEYTYPE_FAST和NX_KEYTYPE_REWIND.如果它们的功能不明显,则NX_KEYTYPE_PLAY是播放/暂停键,NX_KEYTYPE_FAST是下一个键,NX_KEYTYPE_REWIND是前一个键.现在,当按下任何键时没有任何反应,所以让我们回顾一些可能的逻辑.我们将从一个简单的场景开始.

 
  1. case NX_KEYTYPE_PLAY:
  2. print("Play")
  3. break

使用此代码后,当您的应用程序检测到已按下播放/暂停键时,您将看到打印到控制台的“播放”.简单吧?让我们通过调用应用程序的NSApplicationDelegate中的函数来提高效率.首先,我们假设您的NSApplicationDelegate有一个名为printMessage的函数.我们将随时修改它,因此请密切关注这些变化.它们将是次要的,但更改将影响您从mediaEventKey调用它们的方式.

 
  1. func printMessage() {
  2. print("Hello World")
  3. }

这是最简单的情况.调用printMessage()时,您将在控制台中看到“Hello World”.您可以通过调用NSApplicationDelegate上的performSelector来调用它,可以通过MediaApplication访问它. performSelector接受一个Selector,它只是NSApplicationDelegate中函数名称.

 
  1. case NX_KEYTYPE_PLAY:
  2. delegate!.performSelector("printMessage")
  3. break

现在,您将看到打印到控制台的“Hello World”.让我们用一个新版本的printMessage来提升参数.

 
  1. func printMessage(arg: String) {
  2. print(arg)
  3. }

现在的想法是,如果调用printMessage(“Hello World”),您将在控制台中看到“Hello World”.我们现在可以修改performSelector调用来处理传入参数.

 
  1. case NX_KEYTYPE_PLAY:
  2. delegate!.performSelector("printMessage:",withObject: "Hello World")
  3. break

关于这一变化,有几点需要注意.首先,重要的是要注意:已添加到选择器中.这将函数名称与参数发送给委托时将其分开.如何工作并不是太重要,不能记住,但它是委托调用printMessage:“Hello World”的东西.我很确定这不是100%正确,因为它可能会使用某种对象ID,但我没有对细节进行任何广泛的挖掘.无论哪种方式,要记住的重要事情是添加:传入参数时..第二件要注意的是我们添加一个withObject参数. withObject采用AnyObject?作为一种价值.在这种情况下,我们只传入一个String,因为这是printMessage正在寻找的.当您的应用程序检测到已按下播放/暂停键时,您仍应在控制台中看到“Hello World”.让我们看一个最终的用例:printMessage的一个版本,它不是一个参数,而是两个参数.

 
  1. func printMessage(arg: String,_ arg2: String) {
  2. print(arg)
  3. }

现在,如果调用printMessage(“Hello”,“World”),您将在控制台中看到“Hello World”.我们现在可以修改performSelector调用来处理传递两个参数.

 
  1. case NX_KEYTYPE_PLAY:
  2. delegate!.performSelector("printMessage::",withObject: "Hello",withObject: "World")
  3. break

和以前一样,这里有两件事需要注意.首先,我们现在添加两个:到Selector的末尾.像以前一样,这是委托可以传递包含参数的信息.在一个非常基本的层面上,它看起来像printMessage:“Hello”:“World”,但我再次不知道它在更深层次上的真实情况.需要注意的第二件事是我们在performSelector调用添加了第二个withObject参数.像以前一样,这个withObject采用AnyObject?作为一个值,我们传入一个String,因为这是printMessage想要的.当您的应用程序检测到已按下播放/暂停键时,您仍应在控制台中看到“Hello World”.

最后要注意的是performSelector最多只能接受两个参数.我真的很想看到Swift添加像splatting或varargs这样的概念,这样这种限制最终会消失,但是现在只是避免尝试调用需要两个以上参数的函数.

这就是一个非常简单的MediaApplication类,只要你完成上面的所有内容,就会打印出一些文本:

 
  1. import Cocoa
  2. class MediaApplication: NSApplication {
  3. override func sendEvent(event: NSEvent) {
  4. if (event.type == .SystemDefined && event.subtype.rawValue == 8) {
  5. let keyCode = ((event.data1 & 0xFFFF0000) >> 16)
  6. let keyFlags = (event.data1 & 0x0000FFFF)
  7. // Get the key state. 0xA is KeyDown,OxB is KeyUp
  8. let keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA
  9. let keyRepeat = (keyFlags & 0x1)
  10. mediaKeyEvent(Int32(keyCode),keyRepeat: Bool(keyRepeat))
  11. }
  12. super.sendEvent(event)
  13. }
  14. func mediaKeyEvent(key: Int32,keyRepeat: Bool) {
  15. // Only send events on KeyDown. Without this check,these events will happen twice
  16. if (state) {
  17. switch(key) {
  18. case NX_KEYTYPE_PLAY:
  19. print("Play")
  20. break
  21. case NX_KEYTYPE_FAST:
  22. print("Next")
  23. break
  24. case NX_KEYTYPE_REWIND:
  25. print("Prev")
  26. break
  27. default:
  28. break
  29. }
  30. }
  31. }
  32. }

现在,我还要补充一点,认情况下,应用程序在运行时将使用标准的NSApplication.如果您想使用整篇文章所涉及的MediaApplication,您需要继续修改应用程序的Info.plist文件.如果您在图形视图中,它将如下所示:

否则,它看起来像这样:

 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3. <plist version="1.0">
  4. <dict>
  5. <key>CFBundleDevelopmentRegion</key>
  6. <string>en</string>
  7. <key>CFBundleExecutable</key>
  8. <string>$(EXECUTABLE_NAME)</string>
  9. <key>CFBundleIconFile</key>
  10. <string></string>
  11. <key>CFBundleIdentifier</key>
  12. <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
  13. <key>CFBundleInfoDictionaryVersion</key>
  14. <string>6.0</string>
  15. <key>CFBundleName</key>
  16. <string>$(PRODUCT_NAME)</string>
  17. <key>CFBundlePackageType</key>
  18. <string>APPL</string>
  19. <key>CFBundleShortVersionString</key>
  20. <string>1.0</string>
  21. <key>CFBundleSignature</key>
  22. <string>????</string>
  23. <key>CFBundLeversion</key>
  24. <string>1</string>
  25. <key>LSApplicationCategoryType</key>
  26. <string>public.app-category.utilities</string>
  27. <key>LSMinimumSystemVersion</key>
  28. <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
  29. <key>lsuielement</key>
  30. <true/>
  31. <key>NSHumanReadablecopyright</key>
  32. <string>copyright © 2015 Chris Rees. All rights reserved.</string>
  33. <key>NSMainNibFile</key>
  34. <string>MainMenu</string>
  35. <key>nsprincipalClass</key>
  36. <string>NSApplication</string>
  37. </dict>
  38. </plist>

在任何一种情况下,您都需要更改nsprincipalClass属性.新值将包含项目的名称,因此它将类似于Notify.MediaApplication.完成更改后,运行您的应用程序并使用这些媒体键!

总结

以上是编程之家为你收集整理的macos – 在Swift中捕获OSX媒体控制按钮全部内容。

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给好友。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值