参考自:一篇搞定事件传递、响应者链条、hitTest和pointInside的使用
事件的传递,主要是理解UIView两个方法
1.hitTest:withEvent:
2.pointInside:withEvent:
如图1,绿色(CustomViewB)和黄色(CustomViewC)是黑色(CustomViewA)的子视图,白色(CustomViewD)是黄色的子视图
在每个自定义的视图内重写以下三个方法
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {
NSLog(@"%s", __func__);
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"%s", __func__);
// 是否是可以点击的
if (!self.userInteractionEnabled || self.alpha < 0.01) return nil;
// 判断触摸点是否在自己范围内
if(![self pointInside:point withEvent:event]) return nil;
// 判断是否还有更合适的视图
NSInteger count = self.subviews.count;
for (NSInteger i = (count - 1); i >= 0; i--) {
UIView *childView = self.subviews[i];
CGPoint chindPoint = [self convertPoint:point toView:childView];
UIView *fileView = [childView hitTest:chindPoint withEvent:event];
if (fileView) {
return fileView;
}
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL result = CGRectContainsPoint(self.bounds, point);
NSLog(@"%d-%s", result, __func__);
return result;
}
一:响应事件分析
1.点击3区域(1,2,4,7区域类似)
2017-06-23 12:42:14.763 sfasd[11534:6414261] -[CustomViewA hitTest:withEvent:]
2017-06-23 12:42:14.763 sfasd[11534:6414261] 1--[CustomViewA pointInside:withEvent:]
2017-06-23 12:42:14.764 sfasd[11534:6414261] -[CustomViewC hitTest:withEvent:]
2017-06-23 12:42:14.764 sfasd[11534:6414261] 1--[CustomViewC pointInside:withEvent:]
2017-06-23 12:42:14.764 sfasd[11534:6414261] -[CustomViewD hitTest:withEvent:]
2017-06-23 12:42:14.765 sfasd[11534:6414261] 0--[CustomViewD pointInside:withEvent:]
2017-06-23 12:42:14.765 sfasd[11534:6414261] -[CustomViewA hitTest:withEvent:]
2017-06-23 12:42:14.765 sfasd[11534:6414261] 1--[CustomViewA pointInside:withEvent:]
2017-06-23 12:42:14.769 sfasd[11534:6414261] -[CustomViewC hitTest:withEvent:]
2017-06-23 12:42:14.773 sfasd[11534:6414261] 1--[CustomViewC pointInside:withEvent:]
2017-06-23 12:42:14.774 sfasd[11534:6414261] -[CustomViewD hitTest:withEvent:]
2017-06-23 12:42:14.774 sfasd[11534:6414261] 0--[CustomViewD pointInside:withEvent:]
2017-06-23 12:42:14.776 sfasd[11534:6414261] -[CustomViewC touchesBegan:withEvent:]
点在3区域,黑色视图先判断点击的点也位于自己的范围内,看看有没有比黑色视图更合适的响应位置,遂倒序遍历自己的子视图;然后黄色视图判断是否在自己的子视图内,发现点击区域位于子视图之外,则没找到更合适的响应视图,只好返回自己;这样一层层的返回,黑色视图返回黄色视图给上一级的视图....最后,最佳的响应视图便是黄色视图,所以[CustomViewC touchesBegan:withEvent:]
2.点击6区域(区域5类似)
2017-06-23 12:55:25.946 sfasd[11534:6414261] -[CustomViewA hitTest:withEvent:]
2017-06-23 12:55:25.947 sfasd[11534:6414261] 1--[CustomViewA pointInside:withEvent:]
2017-06-23 12:55:25.947 sfasd[11534:6414261] -[CustomViewC hitTest:withEvent:]
2017-06-23 12:55:25.947 sfasd[11534:6414261] 0--[CustomViewC pointInside:withEvent:]
2017-06-23 12:55:25.947 sfasd[11534:6414261] -[CustomViewB hitTest:withEvent:]
2017-06-23 12:55:25.948 sfasd[11534:6414261] 0--[CustomViewB pointInside:withEvent:]
2017-06-23 12:55:25.948 sfasd[11534:6414261] -[CustomViewA hitTest:withEvent:]
2017-06-23 12:55:25.948 sfasd[11534:6414261] 1--[CustomViewA pointInside:withEvent:]
2017-06-23 12:55:25.950 sfasd[11534:6414261] -[CustomViewC hitTest:withEvent:]
2017-06-23 12:55:25.950 sfasd[11534:6414261] 0--[CustomViewC pointInside:withEvent:]
2017-06-23 12:55:25.951 sfasd[11534:6414261] -[CustomViewB hitTest:withEvent:]
2017-06-23 12:55:25.952 sfasd[11534:6414261] 0--[CustomViewB pointInside:withEvent:]
2017-06-23 12:55:25.954 sfasd[11534:6414261] -[CustomViewA touchesBegan:withEvent:]
点在6区域,黑色视图先判断点击的点也位于自己的范围内,看看有没有比黑色视图更合适的响应位置,遂倒序遍历自己的子视图;结果都不在自己的子视图内,所以返回自己,[CustomViewA touchesBegan:withEvent:]被调用
结论:1.点击会从父视图倒序遍历自己的自视图,寻找到最合适的响应视图返回;
2.点击子视图的点超出父视图的范围,子视图无法响应,位于点击的点之下的最近的一个视图会响应事件.