在UIWebView中禁用点击延迟

从历史上看,使混合移动应用感到有点“偏离”的差异之一是,使用简单的click事件处理程序处理UI元素上的轻击时会有所滞后。 创建了诸如Fastclick之类的库来通过使用原始触摸事件立即触发事件处理程序来缓解这种情况。 尽管它们用于基本用途,但为触摸事件增加了JavaScript执行开销,这导致了麻烦。

最近,Android上的Chrome和iOS上的Safari都取消了对不可扩展页面的限制。 这是单次点击延迟的根本原因-无法知道用户是尝试双击还是单次点击,因此浏览器必须在第一次点击后等待才能看到如果另一个来了。

我以为这适用于应用程序中嵌入的Web视图,但是我很失望地看到Quip的行为在iOS 9.3或10.0上并没有改善(对于大多数事件处理程序,我们都有自己的类似Fastclick的包装器,但不适用于到复选框,然后仍然很落后)。 进一步的研究表明,该改进不适用于UIWebView (用于将Web视图嵌入iOS应用程序的较旧机制WKWebView较现代,但仍存在一些局限性,因此Quip尚未迁移到该应用程序)。

有关改进的WebKit博客文章包括一些与相关的跟踪错误相关的链接(如前所述, WKWebView是完全开源的,仍然不错)。 深入研究关联的提交之一,这似乎是要调整多个UIGestureRecognizer实例之间的交互。 通常,处理单次点击的操作必须等待处理两次的操作失败,然后才能触发其动作。 由于一次敲击要花费350毫秒来确定一次敲击是否跟随另一次敲击,因此单次敲击需要很长时间才能失败。 Apple所做的更改是为不可缩放的页面禁用了第二个手势识别器。

UIWebView不是开源的,但我认为其实现必须类似。 为了验证这一点,我添加了一个小代码段以转储其视图层次结构的所有手势识别器(由[self dumpGestureRecognizers:uiWebView level:0]触发:

  -(void)dumpGestureRecognizers:[UIView *)view level:(int)level { 
NSMutableString *前缀= [NSMutableString新];
for(int i = 0; i <level; i ++){
[前缀appendString:@“”];
}
NSLog(@“%@视图:%@”,前缀,视图);
如果(view.gestureRecognizers.count){
NSLog(@“%@ gestureRecognizers”,前缀);
的(UIGestureRecognizer * gestureRecognizer in view.gestureRecognizers){
NSLog(@“%@%@”,前缀,gestureRecognizer);
}
}
对于(UIView * view.subviews中的subview){
[self dumpGestureRecognizers:subview级别:level +1];
}
}

这表明UIWebView包含一个UIScrollView ,后者又包含一个UIWebBrowserView 。 该视图有几个手势识别器,最有趣的是UITapGestureRecognizer ,它只需单击一下即可轻敲,并具有_singleTapRecognized选择器作为动作。 果然,它要求另一个接受两次轻击的手势识别器发生故障(它的动作设置为_doubleTapRecognized ,这进一步使其目的明确了)。

  <UITapGestureRecognizer:0x6180001a72a0; 
状态=可能;
视图= ;
target = <(action = _singleTapRecognized :, target = )>;
必须失败= {
<UITapGestureRecognizer:0x6180001a7d20;
状态=可能;
视图= ;
target = <(action = _doubleTapRecognized :, target = )>;
numberOfTapsRequired = 2>,
<UITapGestureRecognizer:0x6180001a8180;
状态=可能;
视图= ;
target = <(action = _twoFingerDoubleTapRecognized :, target = )>;
numberOfTapsRequired = 2; numberOfTouchesRequired = 2>
}>

作为实验,我然后添加了一个片段来禁用此双击识别器:

 对于(webView.scrollView.subviews中的UIView *视图){ 
如果([view.class.description equalsString:@“ UIWebBrowserView”]){
的(UIGestureRecognizer * gestureRecognizer in view.gestureRecognizers){
如果([gestureRecognizer isKindOfClass:UITapGestureRecognizer.class]){
UITapGestureRecognizer * tapRecognizer =(UITapGestureRecognizer *)poseRecognizer;
如果(tapRecognizer.numberOfTapsRequired == 2 && tapRecognizer.numberOfTouchesRequired == 1){
tapRecognizer.enabled =否;
打破;
}
}
}
打破;
}
}

完成此操作后,将立即分派click事件,并且延迟最小。 我创建了一个简单的测试平台,显示了带有手势识别器的常规UIWebViewWKWebView和“被黑客入侵”的UIWebView之间的区别。 尽管WKWebView仍然要快几毫秒,但情况要好得多。

请注意, UIWebBrowserView是私有类,因此对其进行引用可能会导致App Store拒绝。 您可能需要寻找检测手势识别器的替代方法。 Quip已经使用此hack运行了几个月,没有任何不良影响。 我唯一的遗憾是,我没有想到这一点,我们(和其他混合应用程序)本可以获得多年无延迟的点击。

该帖子最初出现在 persistent.info上