UITableView 手势延迟导致 subview 无法完成两次绘制
问题:
在UITableViewCell 中点击自定义View 本来想在touchesBegan和touchesEnd中各触发一次绘制来模拟点击高亮的效果,但只要是快速点击就无法触发高亮效果,从而探究原因。
解释:
UIScrollView 中默认情况下对TouchesBegan进行了延迟(150ms)用于判断是否进行滑动如果滑动就不将事件传递给子view;由于延迟导致如果点击过快,这时候touchesBegan 和 touchesEnd就紧接着发生。
如果是在touchesBegan和touchesEnd里面都进行了 setNeedsDisplay 操作,标记需要绘制;而两次的间隔时间极小,导致Runloop 只执行一次绘制。换句话说同时标记了两次只有一次标记绘制生效。
iOS 的屏幕刷新为60FPS,也就是最多每秒发生60次的绘制。 每一帧间隔就是 1000 ms / 60 = 16.66 ms ,当两次的绘制的间隔少于16.6毫秒时无法完成两次绘制只会触发一次绘制。
探究:
打印tableview默认手势
如下:
可以看到一个UIScrollViewDelayedTouchesBeganGestureRecognizer,这个是私有的手势,在UIKit库中可以看到,查看一下头文件定义
其中关于手势延迟的处理UIDelayedAction 由这个类实现(是从名字猜想),再找这个UIDelayedAction 看看,在下面
[https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UIDelayedAction.h][2]
看到有个NSTimer,估计就是通过这个Timer来计时,然后再延迟传递事件。看到它的初始化方法有delay:(double)arg4 应该就是延迟多少时间了吧。
为了验证这个猜想,想办法hook 一下这两个函数中的其中一个才行。
用以上代码在启动应用时调用 [UIDelayedActionHook hook] ; 运行。
发现会调用两次:
第一次调用不知道是干啥的,长按手势消失时间?
第二次才是我们想要的,确实是和手势
UIScrollViewDelayedTouchesBeganGestureRecognizer 相关的;延迟时间是0.149秒,也就是上面提到的150毫秒,看来确实是这样。
我在进一步测试,我在自定义的view(次view就是放在UITableViewCell上的)上打印touchesBegan 和 touchesEnded 的间隔时间
发现才间隔0.31ms,离16.7 ms 还差很远呢,所以快速点击时永远都不会触发两次绘制。
也就是因为UIScrollViewDelayedTouchesBeganGestureRecognizer 把事件延迟(150ms)传递给自定义view,而这个时候手指都快要离开屏幕了,随后马上就触发touchesEnded,所以导致间隔时间很短,无法完成两次绘制。
自定义view放在普通View上时,touchesBegan 和 touchesEnded的时间间隔:
![8E3932E4-39D8-411D-9102-65624A01D75C.png](http://upload-images.jianshu.io/upload_images/139521-3d67a0d116ecaf36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
解决办法:
1.关闭UITableview的手势延迟,(但这种做法不怎么好,因为这样对滑动手势有影响)
2.在自定义的view中延迟执行绘制,相关的条件也要延迟获取(比如:touchesEnded 中获取点击的point等)。
参考资料:
[http://pinka.cn/2015/06/uiwebview的scrollview和渲染机制探究/][7]
[2]: https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UIDelayedAction.h
版权声明: 本文为 InfoQ 作者【AlienJunX】的原创文章。
原文链接:【http://xie.infoq.cn/article/2e39c26f963377f9bf28fc478】。文章转载请联系作者。
评论