触摸事件处理的详细过程:
当用户点击屏幕后产生一个触摸事件,经过经过一系列的传递过程后,会找到最合适的视图控件来处理这个事件,找到最合适的视图之后,就会调用空间的touches那三个方法,这些方法的默认做法是把事件顺着响应者链条向上传递,将事件传递给上一个响应者进行处理。
传递过程
UIApplication
接收到事件,将事件传递给Window
。Window
遍历subViews
的hitTest:withEvent:
方法,找到点击区域内合适的视图来处理事件。UIView
的子视图也会遍历其subViews
的hitTest:withEvent:
方法,以此类推。- 直到找到点击区域内,且处于最上方的视图,将视图逐步返回给
UIApplication
。 - 在查找第一响应者的过程中,已经形成了一个响应者链。
- 应用程序会先调用第一响应者处理事件。
- 如果第一响应者不能处理事件,则调用其
nextResponder
方法,一直找响应者链中能处理该事件的对象。 - 最后到
UIApplication
后仍然没有能处理该事件的对象,则该事件被废弃。
怎么寻找最合适的view?用到以下的两个方法。
// 此方法返回的View是本次点击事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判断一个点是否落在范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
事件传递给窗口或控件的后,就调用hitTest:withEvent:方法寻找更合适的view,如果子控件是合适的view,则在子控件再调用hitTest:withEvent:查看子控件是不是合适的view,一直遍历,直到找到最合适的view,或者废弃事件。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.alpha <= 0.01 || self.userInteractionEnabled == NO || self.hidden) {
return nil;
}
BOOL inside = [self pointInside:point withEvent:event];
if (inside) {
NSArray *subViews = self.subviews;
// 对子视图从上向下找
for (NSInteger i = subViews.count - 1; i >= 0; i--) {
UIView *subView = subViews[i];
CGPoint insidePoint = [self convertPoint:point toView:subView];
UIView *hitView = [subView hitTest:insidePoint withEvent:event];
if (hitView) {
return hitView;
}
}
return self;
}
return nil;
}
总结:
1、首先判断UIControl还是UIResponder;
2、如果是UIResponder -(从上往下) 响应链找到最合适的视图 ;如果是 UIControl- UIApplication (如果类似view1有btn和tap 则响应btn,如果view1有btn,btn上有tap,则响应tap)
3、有手势先手势,同级的话先看有没有UIControl。
tips:
1、手势不参与响应者链传递事件,但是也通过hitTest
的方式查找响应的视图,手势和响应者链一样都需要通过hitTest
方法来确定响应者链的。在UIApplication
向响应者链派发消息时,只要响应者链中存在能够处理事件的手势,则手势响应事件,如果手势不在响应者链中则不能处理事件,处理最合适的view的响应。
2、如果同一视图有UIGesture和UIButton,会响应UIButton的响应事件;
3、如果同一视图添加多个UIGesture,只会响应最上面的。