IMKInputController 的碎碎念

在开发业火输入法的过程中,发现 IMKInputController 有一些文档没提到,又容易搞错的点,记录一下

IMKInputController 的创建和销毁

系统会为每一个输入框实例化一个 IMKInputController 对象,虽然同一时间只有一个输入框能获焦,但是在当前的输入框获焦时,之前的实例并不会立刻销毁,所以同一时间系统中可能存在多个 IMKInputController 实例,而且随着使用实例会越来越多。

关于实例的销毁时机,经过实验发现,当应用程序退出时,由该应用程序的输入框实例化的对象会销毁。所以,一个比较合理的猜测是 IMKInputController 对象的销毁是跟随输入框的生命周期的,当输入框被销毁或释放时,其持有的对象也会随之释放。

IMKInputController 的碎碎念

activateServerdeactivateServer 的调用时机和顺序

当一个输入框获取到输入焦点时,如果当前的输入框是第一次获取焦点,那么会首先实例化一个对象,然后调用对象上的 activateServer 方法,之前的输入框持有的对象的 deactivateServer 方法被调用,这里两个方法的调用顺序并不固定,所以不要依赖调用顺序来做逻辑。

IMKInputController 的碎碎念

safari 的地址栏输入时,有发现一个比较奇怪的问题,当从中文输入模式切换到英文模式下时,原本没有输完的原码应该要自动上屏后清空,但是却发现并没有上屏,而且再切回中文的时候,也会发现原码没有清空。这里我的实现是在 activateServer 时,把当前 IMKInputController 对象绑定给了全局的 shift 按键监听对象(至于原因,下面有说到),然后在 shift 键按下时,调用对象绑定的 IMKInputController 对象的方法把原码上屏并清空。但是在 safari 的地址栏中,当出现问题时,接受原码输入的 IMKInputController 实例和绑定给全局 shift按键监听的 IMKInputController 实例并不是同一个。理论上来说虽然有多个 IMKInputController 实例,但是最后被系统调用 activateServer 方法的实例应该是要和接受原码输入的实例是同一个才对,但是至少目前来看,在 Safari 的地址栏中并不是这样(MacOS 版本 13.3.1, Safari版本待补充),所以这里最好是在接收原码输入时,重新再绑定一下。

IMKInputController 的碎碎念

shift 按键的监听

shift 按键常被用来作为输入法中英文模式的切换按键。

官方的给出做法是在 recognizedEvents 方法中返回包含 flagschanged ,如此以来就能监听到所有修饰键(比如 command, shift, alt, fn 等), 然后在事件对象中判断 shift 按键,这里要注意的是 flagsChanged 事件虽然是由按键引起的,但是并不会触发 keydownkeyup 事件,所以只监听 keydown 事件是不行的,必须要监听 flagsChanged 事件。

关于 flagsChanged 事件的触发时机: shift 按键按下和抬起都会触发 flagschanged 事件,在按下的事件中可以在具体的事件对象中的字段来区分 shift 按键和其他的修饰键,但是我们不应该在键的按下中去做逻辑,这是因为 shift 键还被用来和英文键组合输出大写字母,所以如果在 shift 键被按下时来做中英文的切换,那就会和写字母的输出冲突。同样的道理,如果在按键抬起时做切换的逻辑也会有同样的问题,而且如果在 shift 键抬起中做切换的逻辑,除了前面的问题外,也无法根据抬起按键的事件对象区分 shift 和其它按键(因为事件 flagsChanged 的事件对象中的变化值为0,即从某个修饰键变为了0,但是某个修饰键并不在事件对象中)。
除了上面的问题,在实际运行中发现还存在一些问题,在一些输入框中即使按了 shift 键,但是事件并有触发,也就是说监听代码没有被执行,shift 按钮从未被按下,目前发现的场景有,在保存文件时调起的系统保存输入框中和 LaunchPad 的搜索输入框中。
现在感觉到复杂了吗?

处理如何监听的问题

因为 shift 按键可以和其它按键组合输出大写字母或标点符号,所以只单独监听 shift 按键的按下或抬起是不行的,需要监听单独 shift 按键的按下和抬起。也就是说需要监听按下和抬起两个动作,并且在按下和抬起的动作中不能有其它的按键操作,同时,需要对按下和抬起的时间间隔做要求,不能太长,太长的话,就不做切换,当然也不能太短

处理某些场景下 shift 按键不触发的问题

因为使用 recognizedEvents 的方法,在某些系统的输入框中监听不到 shift 按键,又不想对某些场景做特殊逻辑,所以需要找到一种更稳定的方法来监听 shift 按键。这里可以使用 NSEvent.addGlobalMonitorForEvents 方法来做监听,从实践来看,能够解决上面的问题。

但是需要注意的是,NSEvent.addGlobalMonitorForEvents 只能监听到其它应用的按键事件,但是监听不到应用本身的事件,所以如果输入法应用内有输入文字的场景就会有问题,业火输入法的用户词库场景就遇到了这个问题。解决这个问题也比较简单,可以结合 NSEvent.addGlobalMonitorForEventsNSEvent.addLocalMonitorForEventsNSEvent.addGlobalMonitorForEventsrecognizedEvents 两种方法,业火输入法使用的是后者

小小的总结

有两个点后续要注意

  1. 在开发过程中,有些点过于相当然了,比如说 IMKInputController 的创建和销毁,刚开始我以为是当前的聚焦时,之前的就会销毁,根据一些测试情况,就认为 activateServerdeactivateServer 有固定的执行顺序。但是官方文档上并没有任何地方提及到这一点,所以这里的一些实现有些相当然。

  2. MacOS系统或系统应用的特殊情况太多,比如说某些系统界面下 shift 不生效的问题,还有 Safari 地址栏的奇葩行为,这些没有官方说明,只能是在实践中发现这些坑,修修补补

原文链接:https://juejin.cn/post/7225790525437673529 作者:扰乱

(0)
上一篇 2023年4月28日 上午10:51
下一篇 2023年4月28日 上午11:01

相关推荐

发表回复

登录后才能评论