我想实现,将在服务时选择了文本呈现的上下文菜单项。因此,例如,如果我在TextEdit中选择了一个单词,我希望在上下文菜单中显示一个菜单项“ Do my stuff”,它将选择的单词馈送到我的应用程序代码中以进行进一步处理。
通过仔细研究,我得出结论,我需要实施和注册服务。我尝试遵循提供服务的文档,但这似乎至少有些过时(更不用说在某些地方含糊)。
据我了解,我能够做到以下几点:
我实现了服务对象:
import Foundation
import AppKit
class ContextualMenuServiceProvider {
func importString(_ pasteBoard: Pasteboard, userData: String, error: String) {
print(">>>> in import string from service consumer")
}
}
我在Info.plist中为服务创建了一个条目:
<key>NSServices</key>
<array>
<dict>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Import to $(PRODUCT_NAME)</string>
</dict>
<key>NSMessage</key>
<string>importString</string>
<key>NSPortName</key>
<string>MyApp</string>
<key>NSSendTypes</key>
<array>
<string>public.plain-text</string>
</array>
</dict>
</array>
最后,我尝试在AppDelegate中注册该服务。现在文档说要使用:
NSApp.setServicesProvider(encryptor)
// where encryptor is an object of my ContextualMenuServiceProvider
但是,似乎NSApp没有setServicesProvider
方法。我尝试使用:
NSApp.servicesProvider = ContextualMenuServiceProvider()
属性,但是,这似乎也不起作用。
我通过从Xcode运行应用程序来对其进行测试,然后在应用程序运行时,我尝试在TextEdit中选择一些文本(不应该局限于TextEdit,我仅以它为例),然后右键单击我正在寻找我的菜单项。这没用。
我能够找到某种类似的SO问题(例如,在Delphi XE2中创建os x服务或如何向Mac OS Finder中添加菜单项),但是我无法从中检测到我做错了什么(不是提及其中大多数是较旧的(使用setServiceProvider
),或使用C#或Delphi之类的语言)。
知道我的代码/方法有什么问题吗?
好的,看来我犯了几个小错误,使我相信它不起作用。但是,最终我能够使它起作用。因此,如果您面临相同的任务,这是Swift 3中的解决方案:
(1)您必须实现一种服务方法:
import Cocoa
class ServiceProvider: NSObject {
let errorMessage = NSString(string: "Could not find the text for parsing.")
@objc func service(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString>) {
guard let str = pasteboard.string(forType: NSStringPboardType) else {
error.pointee = errorMessage
return
}
let alert = NSAlert()
alert.messageText = "Hello \(str)"
alert.informativeText = "Welcome in the service"
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
我以前不知道的重要事情是提供服务的对象必须子类化NSObject(也可以是ViewController或任何其他NSObject子类)。Services API使用选择器来调用它,选择器技术仅适用于NSObject。
此外,service
方法必须有这样的声明:它需要3个参数,第一个是NSPasteboard
(可以使用Pasteboard
,但不提供string
方法)并没有标注,第二个是可选的String
标记userData
,三是AutoreleasingUnsafeMutablePointer<NSString>
标签error
。遵循此要求以在保持其正常工作的同时获得最佳的类型安全性。否则,服务API将无法找到并调用它。您可以使用UnsafeRawPointers
其所有参数,但不会因此获得任何收益。
(2)在应用程序委托中(或文档说您可以在任何地方都可以做,但是我在这里做),您可以注册服务提供者:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSApplication.shared().servicesProvider = ServiceProvider()
NSUpdateDynamicServices()
}
}
它似乎也可以不NSUpdateDynamicServices
打电话而工作,但是我想最好还是要后悔-它应该刷新系统中的服务,以便您不必注销并登录用户即可获取当前的服务版本。
(3)最后,您必须配置Info.plist来通告您的服务方法:
<key>NSServices</key>
<array>
<dict>
<key>NSMessage</key>
<string>service</string>
<key>NSPortName</key>
<string>serviceTest</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Test hello world</string>
</dict>
<key>NSRestricted</key>
<false/>
<key>NSRequiredContext</key>
<dict/>
<key>NSSendTypes</key>
<array>
<string>NSStringPboardType</string>
</array>
</dict>
</array>
这是Info.plist的XML源-尽管Apple建议使用其编辑器,但在Xcode 8.2中,我无法NSRequiredContext
通过编辑器添加密钥-我不得不将文件作为源代码打开并手动添加。我建议直接编辑XML源。
您可以在“服务属性”中找到有关每个键的含义的文档,但我想提及一些关键点。首先,您需要NSRequiredContext
密钥-他们在文档中提到了它,但是编辑器不支持它-直接编辑XML并将其添加为空(就像我一样)。其次,如果您正在使用NSSendTypes
或NSReturnTypes
并且想要使用字符串,则NSStringPboardType
即使其文档说它已被弃用,也应使用,而应使用代替NSPasteboardTypeString
-后者将不起作用。最后,NSMessage
带有service
值的键是服务方法的名称。因此,我的服务提供者对象声明以下方法:
@objc func service(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString>)
我正在使用中的service
值NSMessage
。如果要更改它(例如,您想要几种服务方法),只是不要忘记这两个必须匹配(Info.plist配置中的值和服务方法的名称)。
(4)在这种状态下,它应该可以工作。我想提到的一件事可能会帮助您进行调试。通过运行以下命令,使用文档最后提到的方法对其进行测试:
/Applications/TextEdit.app/Contents/MacOS/TextEdit -NSDebugServices com.mycompany.myapp
从终端运行此命令应报告是否已注册使用提供的捆绑软件的任何服务,以及是否将其呈现-例如,在我的情况下,问题是我未提供必需的NSRequiredContext
。使用这种方法对其进行测试之后,我能够识别出该服务已安装,并且问题在于服务API假定应将其过滤掉。经过一些实验和谷歌搜索,我通过添加空值解决了它NSRequiredContext
。
每次更改服务后,我建议退出TextEdit并再次运行它,似乎它保留了对旧服务提供者对象的引用(或类似的东西,如果我不重新启动它,则TextEdit不会注册更改)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句