2017-01-03 13 views
2

テキストを選択したときにサービスで表示されるコンテキストメニュー項目を実装しようとしています。たとえば、テキストエディットで単語を選択した場合、コンテキストメニューで「自分のものを行う」メニュー項目を表示して、選択した単語をアプリケーションコードにフィードして処理させます。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に制限する必要はありません)。右クリックしてメニュー項目を探しています。それは動作しません。

類似のSOの質問(例:Creating an os x service、またはHowto 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サブクラスでもあります)。サービスAPIはセレクタを使用してそれを呼び出し、セレクタテクノロジはNSObjectのみで動作します。ラベル付けされていない、それは(あなたがPasteboardを使用することができますが、それはstring方法を提供していない)NSPasteboardである第一、3つの引数を取り、第二第三、userDataラベルオプションStringです:

また、service方法は、この宣言を持たなければなりませんerrorとラベルされたAutoreleasingUnsafeMutablePointer<NSString>。これを続けて、最良の型安全性を得るには、それを動作させます。それ以外の場合、サービス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ソースを直接編集することをお勧めします。

各キーの意味に関するドキュメントはServices Propertiesにありますが、いくつかの重要な点についてお伝えしたいと思います。まず、NSRequiredContextキーが必要です。ドキュメントに記述されていますが、エディタではサポートされていません。XMLを直接編集し、空のままで追加します(私のように)。第二に、NSSendTypesまたはNSReturnTypesを使用していて、文字列を扱いたい場合、documentationには推奨されておらず、NSPasteboardTypeStringで置き換える必要がありますが、NSStringPboardTypeを使用してください。後者は機能しません。最後に、service値のNSMessageキーがサービスメソッドの名前です。私はNSMessageservice値を使用しています

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

:だから、私のサービスプロバイダオブジェクトは、メソッドを次のように宣言します。これを変更したい場合(たとえば、いくつかのサービスメソッドが必要な場合)、これら2つが一致していることを忘れないでください(Info.plist設定の値とサービスメソッドの名前)。

(4)この状態で動作しているはずです。デバッグに役立つと言いたいことが1つあります。実行することによって、最後にdocumentationに記載の方法を使用して、それをテストします。

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

ターミナルからこれを実行すると、提供するバンドルを使用して任意のサービスが登録されているかどうか、また、それが提示されるかどうかを報告しなければならない - 例えば、私の場合には問題は私が義務的なNSRequiredContextを提供していないということでした。このアプローチを使用してテストしたところ、サービスがインストールされたことを認識することができました。問題は、サービスAPIがフィルタリングする必要があると判断されたことでした。いくつかの実験とグーグル・グーグルの後、私は空を追加して解決したNSRequiredContext

サービスを変更するたびに、TextEditを終了してもう一度実行することをお勧めします。古いサービスプロバイダオブジェクトへの参照を保持しているようです(再起動しなければTextEditによって変更が登録されませんでしたそれ)。

関連する問題