iOS9 中的高级触摸处理:合并和预测

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

幸运的是,在 iOS 9 中,Apple 为 UIEvent 引入了一些新功能,以减少触摸延迟并提高时间分辨率。这篇文章有一个演示应用程序,您可以在 我的 GitHub 存储库 中找到它。

触摸合并

第一个特性是触摸合并。如果您的应用正在使用重写的 touchesMoved() 对触摸进行采样,那么您将获得的最大触摸采样数是每秒 60 次,如果您的应用在主线程上进行一些花哨的计算,那么它很可能少于那。

触摸合并允许您访问在 touchesMoved() 调用之间可能发生的所有中间触摸。例如,当您只收到一个 UIEvent 时,这允许您的应用绘制由六个点组成的平滑曲线。

语法非常简单。在我的演示应用程序中,我使用了一些 CALayer。第一层( mainDrawLayer ,其中包含胖乎乎的蓝线)是使用来自 touchesMoved() 的主要 UIEvent 信息绘制的:


 override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        super.touchesMoved(touches, withEvent: event)
    guard let touch = touches.first, event = event else

    {
        return
    }
    mainDrawPath.addLineToPoint(touch.locationInView(view))

    mainDrawLayer.path = mainDrawPath.CGPath

    [...]

如果用户在屏幕上移动手指的速度足够快,他们会看到一条锯齿状的线条,这可能不是他们想要的。为了访问中间触摸,我使用事件的 coalescedTouchesForTouch 方法返回一个 UITouch 数组:


 override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        super.touchesMoved(touches, withEvent: event)
    guard let touch = touches.first, event = event else

    {
        return
    }
    mainDrawPath.addLineToPoint(touch.locationInView(view))

    mainDrawLayer.path = mainDrawPath.CGPath

    [...]

在这里,我遍历这些触摸(我跟踪触摸次数到控制台以获取信息)并通过将 oncoalescedDrawLayer 绘制为叠加层来创建更细的黄线。我在 touchesMoved 方法中添加了一个可怕的循环来减慢速度:


 override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        super.touchesMoved(touches, withEvent: event)
    guard let touch = touches.first, event = event else

    {
        return
    }
    mainDrawPath.addLineToPoint(touch.locationInView(view))

    mainDrawLayer.path = mainDrawPath.CGPath

    [...]

如果你在你的 iOS 9 设备上运行这个应用程序并涂鸦你可以看到两个结果:一条锯齿状的粗蓝线和一条漂亮光滑的细黄线基于所有那些可能被错过的中间触摸事件。

预测触摸

可能更聪明的是触摸预测,它可以让你抢占用户手指(或 Apple Pencil)在不久的将来可能出现的位置。预测触摸使用一些高度调整的算法,并根据 iOS 预计用户在未来大约一帧内触摸的位置不断更新。这意味着您可以在实际需要帮助减少延迟之前开始准备用户界面组件(例如开始淡入)。

在我的演示应用程序中,我将预测的触摸显示为灰色圆圈。语法与合并触摸的语法没有什么不同:该事件有一个新方法 predictedTouchesForTouch() ,它返回一个 UITouch 数组:


 override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        super.touchesMoved(touches, withEvent: event)
    guard let touch = touches.first, event = event else

    {
        return
    }
    mainDrawPath.addLineToPoint(touch.locationInView(view))

    mainDrawLayer.path = mainDrawPath.CGPath

    [...]

您可以在上面的屏幕截图中看到一些灰色圆圈,其中显示了 iOS 预测我会完成螺旋形的两次触摸——我实际上从未这样做过!幽灵般的!

结论

由于用户需要更少的延迟和更高的触摸分辨率,触摸合并和预测使我们的应用程序能够支持这一点。无论我们的应用程序的帧速率如何,我们都可以响应用户的所有手势,甚至抢占其中的一些!

此演示应用程序的源代码可在 我的 GitHub 存储库 中找到。