如何在 iOS 上写一款输入法?这个问题已经被很多人解答过了。你可以轻易通过 Google 找到一篇详细的教程。但是,在 macOS 上写一款输入法就没那么简单了。
好吧,严格来讲,是指用 Swift 在 macOS 上写一款输入法很难。主要的原因是 从来没有人做过这件事情 。
目前能够获取的资料,除了苹果官方的 API 说明(一点用也没有),以及官方的 demo(很详细但过时了,而且还是用 OC 写的。)之外,几乎什么都没有了。
现在我写这篇文章就是来总结一下,如果你想用 Swift 3 开始写一个 macOS 能用的输入法,需要绕过哪些坑。
Info.plist
对于输入法来说,需要在这个文件当中做一些额外的配置,大部分的配置你只需要对比苹果官方推出的 Demo 即可 —— 它们大部分都是正确的。
有一点没有说明的地方在于,你的 Bundle identifier 需要按照一定的格式来写——也就是说,这个字段中必须包含 inputmethod 这个关键词。
比如说,我的输入法叫这个名字 LogInputMac ,那么我就不能写成 com.logcg.LogInputMac ,这样的话 macOS 就不能把它识别为输入法(就算放进了 /资源库/Input Methods/ 也不行),你必须把 inputmethod 这个关键字加进去,而且最好不要放在最后,应该放在前三个点(.)之内。比如说像我这样: com.logcg.inputmethod.LogInputMac 。
另外,对于 Swift,你还需要做一下单独的操作,如果你就这样将编译好的 APP 放进 /资源库/Input Methods/ ,那么虽然 macOS 可以识别输入法,但实际上不会有任何作用——你的 IMKInputController 是完全不会被加载的。
这是因为 macOS 找不到你的 IMKInputController 类,为此,我们应该把 InputMethodServerControllerClass 和 InputMethodServerDelegateClass 字段的内容改成以 $(PRODUCT_MODULE_NAME). 前缀的形式,也就是说,应该写成这样:
AppDelegate.swift
由于 InputMethodKit 的运作方式与普通 APP 不同,我们需要在应用一开始就初始化
IMKServer 类,大多数人的第一反应就是把它放到
applicationDidFinishLaunching(_ : ) 里去执行,正相反,由于 Swift 没有 main 函数,我们可以直接把它声明为全局变量而不需要先声明再给全局变量赋值,我们把它写在
applicationDidFinishLaunching 里,同时你也可以在这里做一些额外的准备工作,比如拷贝一下词库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import Cocoa import InputMethodKit let kConnectionName = "LogInputMac_2_Connection" var server = IMKServer() var candidatesWindow = IMKCandidates() @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { // Insert code here to initialize your application server = IMKServer(name: kConnectionName, bundleIdentifier: Bundle.main.bundleIdentifier) candidatesWindow = IMKCandidates(server: server, panelType: kIMKSingleRowSteppingCandidatePanel, styleType:kIMKMain) LogInputIMEngine.shared.cleanUp() } func applicationWillTerminate(_ aNotification: Notification) { // Insert code here to tear down your application } } |
Print(_:)
由于 Swift 不像 OC 都是用 NSLog 来输出控制台消息,大家都还是喜欢用 Print(_:) 来实现打印终端内容——对于输入法来说,你不能这样调试程序了。
如果你直接用 Xcode 执行你的输入法,是没有任何效果的。想要测试输入法,就必须将它放到 /资源库/Input Methods/ 中然后加载。这样的话,即使你使用 Xcode attach 到输入法的进程,也不会得到任何终端输出。
这时候就需要使用 NSLog ,它可以方便地将输入法的消息输出到控制台中,这样你就可以打开控制台,然后查看进程的信息了。
Debug
对于输入法来说,在 osx 下它是一个全局的东西,所以你很难去对它进行 debug,总之,我们还是可以让开发过程尽可能的舒服,首先,你需要更改输入法目录的权限:
1 |
sudo chmod -R 777 /Library/Input\ Methods |
这样就可以让 Xcode 直接编译到目录了,然后,在 Xcode 里编辑 target,
Build Settings > All > Build Locations > Per-configuration Build Products Path > Debug
把对应的值改为: /Library/Input Methods
这样你就可以直接编译,在系统偏好设置中添加一次即可,以后编译都能事实生效,不用来回重启或者注销了。
值得注意的是:千万不要添加断点……
延伸阅读
最后,这里放上唯一一篇我能找到的中文的对 InputMethodKit 介绍的文章,大家可以参考一下(很遗憾,我并没有用到这篇文章,因为找到它的时候我已经踩过了以上所有的坑)。
半年后,我又重启了这个项目,而且又找到了另外一篇非常有用的文章:macOS下基于IMK的输入法(一)——创建步骤
另外,我想你可能还是无法成功的对照 OC 代码来转换 Swift 项目,那么我把这个对应苹果官方“NumberInput_IMKit_Sample-NumberInput_0”的 Swift 项目打包上传,如果需要的话可以下载参考。
本文由 落格博客 原创撰写:落格博客 » Swift 使用 InputMethodKit 写输入法
转载请保留出处和原文链接:https://www.logcg.com/archives/2078.html
文章很有用!可能有些合作机会,请问怎么联系你?
您好,我有一个输入方案的发明专利,想要发行一个输入法的APP,不知你有没有兴趣合作。
你好朋友,抱歉回复的晚了,前段时间比较忙……
合作可以有,不过我只能以外包、兼职等方式和你合作,不做技术入股后期分红的方案,如果你能接受的话,咱就进一步谈谈,看看是一次性付费我按你需求开发交付,还是按工作时间付费进行开发。
当然,将来开发完成后,维护工作和开发新功能等费用,咱们可以另外再谈或者以后再谈的。
至于开发的代码,可以完全归你所有,如果有涉及到我现在再用的算法相关代码,我们可以另行讨论(如果可能,也可以授权互换等等……
我不知道哪里错了,每次启动都会出现[IMKServer _createConnection]: *Failed* to register NSConnection name=com.logcg.inputmethod_Connection错误,您是否遇到过这问题,怎么解决的
检查你代码里的 NSConnection name,也就是 IMKServer(name: kConnectionName, bundleIdentifier: Bundle.main.bundleIdentifier) 里的实际参数和 Info.plist 里 InputMethodConnectionName中设置的是否完全一致。
Thanks for this insightful post! I am also facing these stumbling blocks that you have mentioned as I am trying to write an input method using Swift. This is really sad that there are no input methods available which are written in Swift to learn from. I am having errors when compiling the example you have uploaded named FlyMac. It also doesn’t have the edits you have mentioned in this post like it doesn’t use $(PRODUCT_MODULE_NAME) in InputMethodServerControllerClass property. It’ll be a great help to me if you upload a working example.
Many thanks!
Hi there, in InputMethodServerControllerClass field you need insert your IMKInputController subclass name, in my project, NumberInputController.
The compiling error was that project somehow lost the AppIcon file, however, I have updated the project and now, in Swift 4.2.
https://www.logcg.com/wp-content/uploads/2016/10/FlyMac-1.zip
Very helpful dude! Thank you very much! I was missing the part about $(PRODUCT_MODULE_NAME) before the controller name!
;)
Hello there I am so happy I found your weblog, I really found you by accident, while I
was looking on Aol for something else, Anyhow I
am here now and wokuld just like to say thanks
a lot for a remarkable post and a all round exciting blog (I also love the theme/design),
I don’t have time to broiwse it all at the momernt but I
have saved it and also added your RSS feeds, so when I have time I will be back tto read more, Please do
keep up the superb job.
:) thanks! bro.
Sry cus I just found that I forgot to reply until now… XD
However, hope you enjoy.