落格输入法是如何处理按键消息的

栏目: IOS · 发布时间: 6年前

内容简介:最近更新:30th 四月, 2019要做一款移动设备上的软键盘,那么怎么处理用户的点击位置,就是你遇到的第一个难题,在这个问题上,我也走了很长的路。我把落格输入法开发以来的触控逻辑大致分类为三个阶段,现在分别来讲讲设计思路,希望能够对你有所帮助。

最近更新:30th 四月, 2019

要做一款移动设备上的软键盘,那么怎么处理用户的点击位置,就是你遇到的第一个难题,在这个问题上,我也走了很长的路。

我把落格输入法开发以来的触控逻辑大致分类为三个阶段,现在分别来讲讲设计思路,希望能够对你有所帮助。

第一代触控引擎

显然,对于一个初学者来说,没什么比系统控件更好用的了,功能全,速度也不慢,业务逻辑完善,所以,落格输入法的第一代消息处理就是用的 UIButton TouchUpInside 消息。因为一开始我甚至用的是 xib 构建键盘布局,所以直接使用了 @ IBAction func buttonTouchUpInside ( _ sender : UIButton ) 这样的声明,你一看就懂了,对吧?处理很方便,用户点击了哪个 Button,那么传进来的就是哪个,不需要做额外的判断,系统都帮你搞定了一切。但很快就遇到了第一个问题——按下按钮后程序总要执行一会,于是 UI 就会卡顿,很明显从按下按键到候选出现,会有延迟。

为了避免输入法业务逻辑干扰(实际上是阻挡)UI 更新,我改为在后台处理进一步的消息,实际上你也应该总是这么做——永远不要在主线程处理业务逻辑。

DispatchQueue.global(qos: .userInitiated).async {
            self.input(button:button)
        }

但这样又引入了另一个问题,当用户点击按键间隔太短速度太快时,按键处理的顺序会错乱,于是,我把异步改为同步,这样业务逻辑还是在后台线程处理,但会严格按照调用的顺序依次执行(这确保了用户按键以实际顺序进行处理)

DispatchQueue.global(qos: .userInitiated).sync {
            self.input(button:button)
        }

注意第一行,async → sync。

第二代触控引擎

第一代引擎正常工作了很久,但最终还是遇到了另一个问题:当用户点击更加快的时候,某些后台逻辑处理不正常。

这实际上是由于 UIButton 自身触控处理机制冲突造成的,当用户点击屏幕键盘速度太快,实际上短时间内同时按下了两个按钮,此时主线程自身是互相阻止的,只有当用户两个手指都离开屏幕,消息才会发送,即 @ IBAction func buttonTouchUpInside ( _ sender : UIButton ) 被立即调用两次。在极短时间内,两次连续调用,虽然是有严格顺序的,但每个按键消息都会处理候选栏的刷新,这就需要异步更改 UI,这就导致了一些处理逻辑异常——在 UI 刷新完成之前,下一个消息已经开始执行。

解决的思路有两个,要么把业务逻辑的一些判断改为和 UI 不关联,要么想办法让系统能够处理用户同时按下多个 UIButton 的情况。 ——显然,应该从后者入手,于是,我在开发 落格输入法 X 时做了一个按键缓存机制,它不再使用 TouchUpInside ,而是 TouchDown TouchUp

var pendingKey:UIButton?
@IBAction func keyboardKeyDown(_ sender:UIButton) {
        if sender == pendingKey {return}
        
        if let pending = pendingKey {
            keyboardKeyConfirm(pending)
        }
        pendingKey = sender
    }
 
@IBAction func keyboardKeyUp(_ sender: UIButton) {
        
        guard let pending = pendingKey, sender.tag == pending.tag else {return}
        keyboardKeyConfirm(pending)
        pendingKey = nil
    }
func keyboardKeyConfirm(_ sender:UIButton) {
        buttonTouchUpInside(sender)
    }

注意 buttonTouchUpInside 就是上一代的处理逻辑,并没有什么变化,这么写是为了让你明白我的变更思路,这样当用户按下一个键,那么就立即缓存它,当用户再按下一个键,如果已经有缓存了,那么就处理它,并将新的加入缓存;当用户抬起手指,那么就处理缓存的那个按键。如此一来,所有的按键都会得到处理,并且不会被 TouchUpInside 这个信号阻拦——因为我已经不再使用它了。

由于更改了信号获取源头(从 TouchUpInside 改为 TouchDown + TouchUp ),对用户来说“手感”变化很大。

第三代触控引擎

第二代实际上已经工作的很好,但在 落格输入法 X 上架之前,我们内部测试就发现了另一个问题——实际上并不能说是新发现的,因为它一直都存在,那就是“q”和“p”的问题。在新的 iOS 系统当中,似乎是为了避免和系统手势冲突,iOS 为屏幕边缘的 5 个像素做了保留处理,当你点击到屏幕边缘的时候(即按按键“q”或者“p”时稍微靠边了点), TouchDown 这个消息是不会立即被触发的。

它会被延迟到你抬起手指的那一刻,然后和 TouchUp 一起发送给键盘。这就导致了用户正常打字的时候,遇到这两个位置,总是会感觉“卡顿”了一下,因为视觉和声音反馈上,确实是延迟到你抬起手指的那一刻而不是按下就立即触发。

如果是在 app 当中,你可以这样做:

override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
        return [.left,.right]
    }

但显然,在键盘中这个代码并不生效。总之,为了让即将上架的 落格输入法 X 更加具有竞争力,我只好硬着头皮继续想办法。

我在尝试了很多方案之后,我终于找到了一个能获取信号的控件—— UITapGestureRecognizer

不是用它来进行标准识别点击——这同样是没用的,必须使用它的 func gestureRecognizer ( _ gestureRecognizer : UIGestureRecognizer , shouldReceive touch : UITouch ) -> Bool 这个代理方法,只有它能够越过系统屏蔽,正确获得 TouchDown 调用,而不是被延迟到用户抬起手指的那一刻。

对应地,我又使用 UIView 本身的 func touchesEnded ( _ touches : Set < UITouch > , with event : UIEvent ? ) 来获取 TouchUp 行为,如此一来,就可以参考上一代引擎的逻辑实现了:

class TouchLayer:UIView,UIGestureRecognizerDelegate {
    var tapGR = UITapGestureRecognizer()
    init(keyboard:KeyboardViewController) {
        super.init(frame: CGRect.zero)
        tapGR.delegate = self
        self.addGestureRecognizer(tapGR)
    }
    var currentTouch:UITouch?
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let t = currentTouch {
            touchUpInside(t)
        }
    }
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if let t = currentTouch {
            touchUpInside(t)
        }
        touchDown(touch)
        return true
 
    }
}

其他代码略过不表,这样操作之后,把这个 TouchLayer 覆盖在键盘上方即可——当然,你也可以在你的 Button 子类里进行这样的操作,然后单独处理。这里我则盖在键盘上方,进行统一处理了。

为此,我不得不对键盘的业务逻辑进行了一番调整和重构……直到去年年底,我写了一篇文章 落格输入法 X 是如何处理屏幕边缘延迟问题的

总之,这就是现在线上版本 落格输入法 X 在使用的触控逻辑,目前来看,一切良好。如果说缺点,大概就是从二代升级三代代价实在是太大了,这几乎是 落格输入法 X 与 经典版 的重点区别之一了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Where Wizards Stay Up Late

Where Wizards Stay Up Late

Katie Hafner / Simon & Schuster / 1998-1-21 / USD 16.00

Twenty five years ago, it didn't exist. Today, twenty million people worldwide are surfing the Net. "Where Wizards Stay Up Late" is the exciting story of the pioneers responsible for creating the most......一起来看看 《Where Wizards Stay Up Late》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具