iOS 自定义键盘 左右划动移动光标 实现

我们都直到,第三方输入法比如搜狗输入法有个经典的手势操作——在键盘上左右划动即可移动光标。而这个功能我自己也十分的常用,所以,我想要自己来实现它。

首先我想到的就是 UISwipeGestureRecognizer ,不过结果可想而知,划动一次只能移动一格光标,这可不是我想要的。

看来唯一的办法就是用 UIPanGestureRecognizer 来自己实现了。

最初的想法

最一开始,我想到的就是像捕捉拖动一样来落去手指在键盘上的位置,我只需要获取 x 轴,如果是正的就往右,负的就往左。

代码看起来大概像这样:

因为我只有按照相对方向移动光标的权限,所以只好如此。当然,缺点就是不论移动的快慢,光标的移动速度是与函数调用的频率一致的。

另一个致命的缺点是如果我手指向一个方向划动比较远,再移动回来——这个过程中光标依然会傻傻地超先前的方向继续移动!

改用加速度而不是坐标作为判断

好吧,这次我改用 UIPanGestureRecognizer 内置的 velocityInView 来获取加速度,因为加速度是有方向的!显然,光标傻傻的朝一个方向移动的问题解决了……可是,它还是依靠系统调用频率来决定移动的速度,这就导致了光标移动太快。

对系统调用频率滤波

好在,系统的调用频率是固定的,这样的话我们可以轻易地实现滤波,加一个属性,用来储存调用的次数,当次数达到了指定的值比如调用了100次,我们才真正执行一次函数:

根据移动速度动态改变光标移动速度

这下光标的移动终于显得比较合适了,那么接下来的问题就是光标的移动速度改变了,现在移动速度是固定的,无论你是想要快一点移动还是慢一点移动,都不行。要达到这一目的,我们还得在加速度上做文章。

加速度会根据你手指在屏幕上移动的速度而改变,简而言之就是你划动的越快,那么加速度就越大——这么一来我们就有了两个方案:

  1. 根据加速度改变一次移动光标的字数;
  2. 根据加速度跟边函数调用的频率。

方案 1 比较简单,把加速度计算到个位数然后直接扔给 adjustTextPositionByCharacterOffset(_:) 就好了,可是这有个问题——定位精确度会下降。这里我选择了更类似搜狗输入法的后者,我依旧每次移动光标 1 个字符的距离,只需要改变函数的调用频率——也就是说,改变我们的滤波算法就好了。

现在的滤波只是单纯的每 100 次取 1 次,那么我们提前获取一次加速度,把它算进去试试,由于加速度有方向——你总不能给滤波器增量减回去吧?我们用 abs 全局数学函数来求一下绝对值,再进行处理,同时由于加速度变化巨大,经过多次测试,缩小10倍比较合适,那么代码看起来就是这个样子的:

这样,函数实际的调用频率会由滤波器决定,而滤波器的增量又由你实际划动时在屏幕上产生的加速度来决定,从而实现了光标跟手指几乎同步移动的效果。

误操作兼容

最后,我们再来点收尾的工作。由于识别层在键盘的按钮下边,那么我们就要让它们合作无间才行—— UIPanGestureRecognizer 会立即取消 UIButton 的点击操作,也就是说,如果用户打字比较快,而手指又不小心在键盘上搓了一下,那么系统就会识别为微小但加速度很大的划动而不是点击。

所以,第一步,我们需要取消 cancelsTouchesInView ,给它赋值为 false 或者在图形配置里取消这个的勾选。

光这一点还不够,我们还得继续加大相应的阈值,在滤波器前边再获取拖动的绝对值,如果拖动小于一定的距离,就同样不执行函数,给用户留下一定的缓冲空间,只有用户真的想要划动来移动光标的时候才去移动光标,那么,最终的函数应该是这样的:

 

发布者:R0uter

如非声明,本人所著文章均为原创手打,转载请注明本页面链接和我的名字。

留下评论

电子邮件地址不会被公开。 必填项已用*标注