触摸事件仅在 iOS 9 的左屏幕边缘附近延迟。如何解决?

Posted

技术标签:

【中文标题】触摸事件仅在 iOS 9 的左屏幕边缘附近延迟。如何解决?【英文标题】:Touch events are delayed near left screen edge on iOS 9 only. How to fix it? 【发布时间】:2015-11-09 22:34:02 【问题描述】:

我正在为 ios 开发键盘扩展。在 iOS 9 上,除了键盘左边缘的按键外,按键会立即做出反应。那些反应大约有 0.2 秒的延迟。原因是触摸只是通过延迟传递给UIView,这是我键盘的根视图。在 iOS 8 上没有这样的延迟。

我的猜测是,这种延迟是由一些应该识别打开“正在运行的应用程序屏幕”手势的逻辑引起的。这很好,但键盘上的延迟是不可接受的。有什么办法可以毫不拖延地获得这些事件吗?也许只是在某些UIGestureRecognizer 上将delaysTouchesBegan 设置为false

【问题讨论】:

您找到解决方案了吗?我们遇到了同样的问题。令人沮丧! 我也想为此找到解决方案。 还是没有解决办法? @Rasto 我相信我已经提出了正确的解决方案。关心标记为已解决?) 【参考方案1】:

如果您有权访问viewwindow 属性,则可以访问这些系统手势识别器并将delaysTouchesBegan 设置为false

这是一个 swift 中的示例代码

if let window = view.window,
   let recognizers = window.gestureRecognizers 
   recognizers.forEach  r in
        // add condition here to only affect recognizers that you need to
        r.delaysTouchesBegan = false
   

也相关:UISystemGateGestureRecognizer and delayed taps near bottom of screen

【讨论】:

【参考方案2】:

这适用于使用更高版本 iOS 的任何人(这适用于我的 iOS 9 和 10)。我的问题是由于滑动返回手势干扰了我的 touchesBegan 方法,阻止它在屏幕的最左边缘触发,直到触摸结束,或者系统识别出该移动不是滑动到返回手势。

在控制器的 viewDidLoad 函数中,简单地说:

self.navigationController?.interactivePopGestureRecognizer?.delaysTouchesBegan = false

【讨论】:

【参考方案3】:

iOS11 以来的官方解决方案是覆盖您的UIInputViewControllerpreferredScreenEdgesDeferringSystemGestures

https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys

但是,它似乎至少不适用于 iOS 13。据我了解,这是因为preferredScreenEdgesDeferringSystemGesturesUIInputViewController 中被覆盖时无法正常工作,至少在iOS 13 上是这样。

当您在常规视图控制器中覆盖此属性时,它会按预期工作:

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

不过,UIInputViewController 并非如此。

UPD:看来,手势识别器仍会毫无延迟地获得.began 状态更新。因此,您可以添加自定义手势识别器来处理触摸事件,而不是遵循下面相当混乱的解决方案。

您可以将UILongPressGestureRecognizerminimumPressDuration = 0 快速添加到您的控制视图中进行测试。

另一种解决方案:

我最初的解决方法是在hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 中调用touch down 效果,即使视图的触摸延迟也会调用。

您必须忽略“真正的”触地事件,当它在大约 0.4 秒后触发或与 touch up inside 事件同时触发时。此外,最好仅在测试点位于约 20pt 横向边距内的情况下应用此 hack。

例如,对于一个等于屏幕宽度的视图,实现可能如下所示:

let edgeProtectedZoneWidth: CGFloat = 20

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 
    let result = super.hitTest(point, with: event)

    guard result == self else 
        return result
    

    if point.x < edgeProtectedZoneWidth || point.x > bounds.width-edgeProtectedZoneWidth
    
        if !alreadyTriggeredFocus 
            isHighlighted = true
        
        triggerFocus()
    

    return result


private var alreadyTriggeredFocus: Bool = false

@objc override func triggerFocus() 
    guard !alreadyTriggeredFocus else  return 
    super.triggerFocus()
    alreadyTriggeredFocus = true


override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) 
    super.touchesCancelled(touches, with: event)

    alreadyTriggeredFocus = false


override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    super.touchesEnded(touches, with: event)

    alreadyTriggeredFocus = false

...其中triggerFocus() 是您在touch down 事件上调用的方法。或者,您可以覆盖touchesBegan(_:with:)

【讨论】:

完善您的 IOS 13 解决方案为我完成了这项工作(在 IOS 13 上)

以上是关于触摸事件仅在 iOS 9 的左屏幕边缘附近延迟。如何解决?的主要内容,如果未能解决你的问题,请参考以下文章

Android:隐藏屏幕顶部边缘的视图

iOS 应用程序中的按钮不响应屏幕边缘的触摸

读书笔记iOS-多点触摸事件与界面几何

iOS 屏幕的响应速度如何?

如何在 iOS 中延迟 UISlider 的开始触摸事件?

触摸事件仅在iPad iOS 11.4上的Mobile Safari中滚动时触发“一次”