2017-01-03 124 views
2

我正試圖實現一個上下文菜單項,當選擇一個文本時將在服務中呈現。因此,例如,如果我在TextEdit中選擇了一個單詞,我需要在上下文菜單中顯示一個菜單項「Do my stuff」,該菜單項會將選定的單詞添加到我的應用程序代碼中以供進一步處理。如何在macOS應用程序中註冊來自應用程序的服務?

通過搜索一下,我得出結論,我需要實施和註冊一項服務。我試圖按照 Providing Services 文檔,但似乎至少有點過時(更不要說在某些地方模糊)。

從我的理解,我能夠做到以下幾點:

我實現了服務對象:

import Foundation 
import AppKit 

class ContextualMenuServiceProvider { 

    func importString(_ pasteBoard: Pasteboard, userData: String, error: String) { 
     print(">>>> in import string from service consumer") 
    } 
} 

我創建了Info.plist中的服務的條目:

​​3210

最後,我試圖在AppDelegate中註冊該服務。現在的文件說,使用:

NSApp.setServicesProvider(encryptor) 
// where encryptor is an object of my ContextualMenuServiceProvider 

然而,似乎NSApp表示沒有setServicesProvider方法。我試圖使用:

NSApp.servicesProvider = ContextualMenuServiceProvider() 

財產,但是,這似乎並不工作。我試着從Xcode運行應用程序來測試它,然後,當應用程序運行時,我嘗試在TextEdit中選擇一些文本(它不應該侷限於TextEdit,我只是用它作爲例子),並右鍵單擊後,我正在尋找我的菜單項。這是行不通的。

我能夠找到某種類似的SO問題(例如,Creating an os x serviceHowto add menu item to Mac OS Finder in Delphi XE2),但我無法檢測到我做錯了什麼(更不用說大部分都是舊的setServiceProvider),或使用C#或Delphi等語言)。

任何想法我的代碼/方法有什麼問題?

回答

1

好的,看起來像我犯了幾個小錯誤,導致我認爲它不工作。但是,最終我能夠使其工作。所以,如果你正面臨着同樣的任務,這裏是斯威夫特3的解決方案:

(1)你必須實現服務方法:

import Cocoa 

class ServiceProvider: NSObject { 

    let errorMessage = NSString(string: "Could not find the text for parsing.") 

    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源 - 雖然蘋果推薦使用他們的編輯, Xcode 8.2我無法通過編輯器添加NSRequiredContext密鑰 - 我必須將該文件作爲源代碼打開並手動添加。我會建議直接編輯XML源代碼。

你可以在Services Properties找到關於每個鍵的含義的文檔,但是我想提一些關鍵點。首先,你需要NSRequiredContext鍵 - 他們在文檔中提到它,但編輯器不支持它 - 直接編輯XML並添加它甚至是空的(就像我這樣做)。其次,如果您使用的是NSSendTypesNSReturnTypes,並且您想要使用字符串,請使用NSStringPboardType,即使其documentation表示它已被棄用,應該由NSPasteboardTypeString替代 - 後者將不起作用。最後,NSMessage鍵與service值是服務方法的名稱。所以,我的服務提供者對象聲明如下方法:

func service(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString>) 

我在NSMessage使用service值。如果您想更改它(例如,您需要多個服務方法),請不要忘記這兩者必須匹配(Info.plist配置中的值和服務方法的名稱)。

(4)在這種狀態下它應該工作。有一件事我想提一提,可能會幫助你進行調試。測試使用在年底documentation提到的方法是通過運行:

/Applications/TextEdit.app/Contents/MacOS/TextEdit -NSDebugServices com.mycompany.myapp 

從終端運行此應報告使用提供捆綁的任何服務是否被註冊,並且還是否將提交 - 例如,在我的情況下,問題是我沒有提供強制性的NSRequiredContext。使用這種方法對其進行測試後,我能夠識別該服務已安裝,問題在於服務API假定它應該被過濾掉。經過一些實驗和谷歌搜索,我通過添加空NSRequiredContext來解決它。

在每次更改服務之後,我建議退出TextEdit並重新運行它,它似乎保留對舊的服務提供者對象的引用(或類似的東西,如果我沒有重新啓動,則沒有通過TextEdit註冊更改它)。

相關問題