🍣入力メソッド
すべてのキー入力に対して🍣を入力する入力メソッドを作った。
🍣を入力するだけのInputMethodができた pic.twitter.com/0If6KVNTAT
— mzp (@mzp) 2017年9月16日
コード
https://github.com/mzp/EmojiIM
入力メソッドの構成
入力メソッドによるテキスト入力は、各アプリケーションと入力メソッドが通信することで実現されている。アプリケーションはキー入力を入力メソッドに送信し、入力メソッドがアプリケーションに対してテキスト入力を行なう。
この通信のために入力メソッドはIMKServerクラスを用いてサーバーを起動する。そして各アプリケーションはクライアントとなり、サーバーと通信する。この通信を入力セッション(input session)と呼ぶ。
サーバーはクライアントごとに入力コントローラーを生成する。この入力コントローラーがキー入力からテキスト入力を生成する。
プロジェクトの作成
Xcodeでプロジェクトを作成する。Cocoa Appテンプレートを選ぶ。 Product nameはなんでもいいが、Bundle identifierにinputmethodという文字列を含める必要がある。

起動時の処理
入力メソッドが起動時に各アプリケーションと通信するための IMKServer
を起動する。このとき、クライアントがサーバーに接続する際に使う名前を指定する。
import Cocoa import InputMethodKit private var server: IMKServer? @NSApplicationMain internal class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet private weak var window: NSWindow! func applicationDidFinishLaunching(_ aNotification: Notification) { server = IMKServer(name: "EmojiInputSession", bundleIdentifier: Bundle.main.bundleIdentifier) } }
入力コントローラ
入力コントローラとして使うクラスを定義する。このクラスは IMKInputController
の派生クラスにする必要がある。
このクラスの名前をInfo.plistで指定す。そのため、objc
属性を用いて、Swiftコンパイラによってマングリングされないようにする。
@objc(EmojiInputController) open class EmojiInputController: IMKInputController {
キー入力時に inputText
メソッドが呼ばれる。キー入力を処理したかどうかを真偽値で返す。
func inputText(_ string: String!, client sender: Any!) -> Bool
テキスト入力をしたいアプリケーションは client
引数として渡される。 ただし、 Any
型のままではテキスト入力を行なえないのでIMKTextInput
プロトコルにキャストする。 キャストに失敗した場合は、入力メソッドが処理しなかったことを示すために偽を返す。
guard let client = sender as? IMKTextInput else { return false }
IMKTextInput
の insertText
メソッドを用いて、入力したい文字列を挿入する。
client.insertText("🍣", replacementRange: NSRange(location: NSNotFound, length: NSNotFound))
Info.plist
Info.plistで入力セッションに使う名前や入力コントローラとして使うクラスの名前を指定する。
<?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>$(DEVELOPMENT_LANGUAGE)</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>CFBundleVersion</key> <string>1</string> <key>LSBackgroundOnly</key> <string>YES</string> <key>LSHasLocalizedDisplayName</key> <false/> <key>LSMinimumSystemVersion</key> <string>$(MACOSX_DEPLOYMENT_TARGET)</string> <key>NSHumanReadableCopyright</key> <string>Copyright © 2017 mzp. All rights reserved.</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> <string>NSApplication</string> <!-- 入力セッションに使う名前 --> <key>InputMethodConnectionName</key> <string>EmojiInputSession</string> <!-- 入力コントローラのクラス名 --> <key>InputMethodServerControllerClass</key> <string>EmojiInputController</string> <!-- 入力メソッドのアイコンとして使う画像 --> <key>tsInputMethodIconFileKey</key> <string>InputMethodIcon.tiff</string> <!-- 入力ソース(入力ソースの詳細はよくわからん) --> <key>TISIntendedLanguage</key> <string>ja</string> <!-- 入力ソースに対する識別子 --> <key>TISInputSourceID</key> <string>jp.mzp.inputmethod.EmojiIM</string> <!-- 入力メソッドが対応している言語のリスト --> <key>tsInputMethodCharacterRepertoireKey</key> <array> <string>Hira</string> <string>Kana</string> <string>Latn</string> </array> </dict> </plist>
インストール
ビルドして生成された EmojiIM.app
を ~/Library/Input Methods
にコピーする。Workspace settingsからDerivedDataをworkspace relativeにしておくと楽。 こんな感じでコピーする。
cp -r DerivedData/EmojiIM/Build/Products/Debug/EmojiIM.app ~/Library/Input\\ Methods
System PreferencesのInput Sourcesから追加できる。うまく反映されない場合は、いったんログアウトするとよい。