写了一个简单Demo,需要通过控制台查看打印信息。
响应链

这张图是用来说明响应链如何工作的。在HitTest已经找到最适合视图的前提下,事件从最顶部(UILabel/UITextField/UIButton)沿着实线传递。事件被处理,停止传递。整个链中都没有处理,作为无效事件被舍弃。
HitTest
当系统检查到手指触摸屏幕时,会把触摸事件加到UIApplication的事件队列中,UIApplication从事件队列中取出最新的触摸事件进行分发传递到UIWindow进行处理。 UIWindow会通过hitTest:withEvent:方法寻找触碰点所在的视图,这个过程称之为hit-test view
hit-test用来确定响应者(UIResponder)。可以简单理解为获取到响应区域的最顶层(最后添加)的可以响应的视图
hitTest方法忽略以下的视图:
1. 透明度小于0.01 alpha < 0.01
2. 视图是隐藏的 hidden=YES
3. 用户交互关闭的 userInteractionEnabled = NO
HitTest与响应链如何合作
HitTest找到响应者(UIResponder)后,UIApplication会把事件传递给响应者,触发响应链。
#0 0x000000010153990d in -[TouchView2 touchesBegan:withEvent:]
#1 0x0000000105634240 in -[UIWindow _sendTouchesForEvent:] ()
#2 0x0000000105635bca in -[UIWindow sendEvent:] ()
#3 0x000000010561430e in -[UIApplication sendEvent:] ()
手势与HitTest
当视图上添加手势时,手势的优先级高于响应链。
当我没搞明白手势的优先级时,我有个疑惑,手势的高优先级,只影响响应链还是在hitTest时就产生作用了?
还猜测hitTest碰到手势是不是直接触发手势处理方法,都不在往下传递了。Demo中第一个例子就是为了验证这个问题。从实际处理过程看,手势不影响hitTest.
ps: hitTest会被触发两次。网上搜索这个问题,更低版本都iOS会调用3次,原因未知。
手势与响应链
既然手势不影响hitTest,只验证响应链相关都问题就好。
我验证都过程,完全是试错的过程,没有前后逻辑,直接放出结论。
1. 手势有自己单独的处理逻辑,不是依附在响应链上的。不要把手势作为响应链的角色。
2. 手势优先级高。手势识别成功会取消响应链处理。响应链即使第一时间处理事件,也无法反向取消手势处理过程。
3. UIApplication把点击事件转发响应者时,同时也转发给手势。
[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:]
这里补充我之前的错误想法:响应链传递过程中,判断当前视图有没有手势,有手势先触发手势识别,然后在调用默认touchesBegan方法。这个观点认为子视图处理完事件,父视图的手势不会被触发。与第二条结论冲突。
4. 响应点击的视图,如果既有手势也实现了touchesBegan,两个都会被触发。touches的其他方法会在手势识别后取消。在手势点击事件和touchesBegan中对参数做累加,看到看到每次点击加2.
5. 子视图父视图都有手势时,能从UITouch中获取到手势数组。手势是同时识别的,不是等到上一个手势识别完才识别下一个。数组前面的手势比后面的早识别一点时间。子视图的手势比父视图手势靠前。某一个手势识别成功,其他手势识别过程会被取消。
6. UIControl和手势叠加时,响应的是UIControl,因为UIApplication分发时,直接分发给了UIControl而不是手势。打印一下调用堆栈就能明白。没有写这个demo。
Demo介绍
第二个例子是关于手势和响应链冲突的。touchview1-touchview4 共4个视图。
TouchView1 红色,有手势
TouchView2 绿色,有手势
TouchView3 灰色,无手势
TouchView4 蓝色 ,无手势
一个视图在另一个内部,即为子视图。
验证结论1,2, 点击灰色视图。灰色视图实现了touchesBegan方法,父视图的手势还是被识别了。
验证结论3,4,点击灰色视图。number每次+2.
验证结论5,点击绿色视图。可以从log中看到gestureRecognizerShouldBegan调用时间不同。
蓝色视图,不包含手势的响应链传递。
网友评论