姬長信(Redy)

hitTest和pointInside方法你真的熟吗?

### hittest方法 * 就是用来寻找最合适的view * 当一个事件传递给一个控件/uff0c就会调用这个控件的hitTest方法 * 点击了白色的view/uff1a 触摸事件 -> UIApplication -> UIWindow 调用 /[UIWindow hitTest/] -> 白色view /[WhteView hitTest/] 实验1: 定义 BaseView/uff0c在里面实现方法touchBegan/uff0c监听当前哪个类调用了该方法。 定义KeyWindow/uff0c在里面实现hitTest方法/uff0c监听哪个类调用了该方法/uff0c用来追踪判断哪个view是最合适的view 在控制器的界面上加5个颜色不同的view/uff0c每个view自定义view去实现/uff0c因此在不同的view上的手势就可以由不同的view拦截到。 ![UI效果图](https://fantasticlbp.gitbooks.io/knowledge-kit/assets/Simulator%20Screen%20Shot%20-%20iPhone%206s%20Plus%20-%202017-10-11%20at%2010.14.37.png) ``` //KeyWindow -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ UIView *view = [super hitTest:point withEvent:event]; NSLog(@"fittest->%@",view); return view; } ``` 结果/uff1a 点击了白色1: ``` 2017-10-11 16:48:52.882547+0800 主流App框架[16295:358790] BrownView--hitTest withEvent 2017-10-11 16:48:59.646610+0800 主流App框架[16295:358790] GreenView--hitTest withEvent 2017-10-11 16:48:59.647145+0800 主流App框架[16295:358790] fittest->> 2017-10-11 16:48:59.647575+0800 主流App框架[16295:358790] BrownView--hitTest withEvent 2017-10-11 16:48:59.647702+0800 主流App框架[16295:358790] GreenView--hitTest withEvent 2017-10-11 16:48:59.647880+0800 主流App框架[16295:358790] fittest->> ``` 点击了蓝色3: ``` 2017-10-11 16:49:56.331024+0800 主流App框架[16295:358790] BrownView--hitTest withEvent 2017-10-11 16:49:56.331335+0800 主流App框架[16295:358790] BView--hitTest withEvent 2017-10-11 16:49:56.331617+0800 主流App框架[16295:358790] BlueView--hitTest withEvent 2017-10-11 16:49:56.331968+0800 主流App框架[16295:358790] YellowView--hitTest withEvent 2017-10-11 16:49:56.333206+0800 主流App框架[16295:358790] fittest->> 2017-10-11 16:49:56.333633+0800 主流App框架[16295:358790] BrownView--hitTest withEvent 2017-10-11 16:49:56.333762+0800 主流App框架[16295:358790] BView--hitTest withEvent 2017-10-11 16:49:56.333893+0800 主流App框架[16295:358790] BlueView--hitTest withEvent 2017-10-11 16:49:56.334005+0800 主流App框架[16295:358790] YellowView--hitTest withEvent 2017-10-11 16:49:56.334185+0800 主流App框架[16295:358790] fittest->> 2017-10-11 16:49:56.334644+0800 主流App框架[16295:358790] BlueView ``` 那么看出来hitTest方法的作用就是找出最合适的view/uff0c那么我们可以指定任何事情的最合适的view为特定的view 实验2: 在KeyWindow中hitTest方法中返回BlueView/uff0c那么点击任何色块的view那么都会交给BlueView去处理事件。 ``` //KeyWindow -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ return self.subviews.firstObject.subviews.firstObject; } ``` 结果/uff1a ``` 2017-10-11 22:48:46.102793+0800 主流App框架[21498:749663] GreenView 2017-10-11 22:48:46.668595+0800 主流App框架[21498:749663] GreenView ``` 因为事件的响应者链条就是当用户操作屏幕会产生一个事件/uff0c该事件被系统加入到事件队列中去/uff0cUIApplication对象会将事件队列中最早加入进去的事件传递给window/uff0c然后window找到最合适的view去处理事件。因此任何事件都会先通过KeyWindow对象去判断并找到最合适的view ## 2个重要的方法 * -/(BOOL/)pointInside:/(CGPoint/)point withEvent:/(UIEvent /*/)event/uff1a 用来判断触摸点是否在控件上 * -/(UIView /*/)hitTest:/(CGPoint/)point withEvent:/(UIEvent /*/)event/uff1a 用来判断控件是否接受事件以及找到最合适的view ## 模仿系统实现找出最合适的view ``` //KeyWindow /** 模仿系统实现寻找最合适的view步骤 1、控件接收事件 2、触摸点在自己身上 3、从后往前遍历子控件/uff0c重复前面2个步骤 4、如果没有符合条件的子控件/uff0c那么就自己最合适 */ -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha = 0; index--) { CGPoint childViewPoint = [self convertPoint:point toView:self.subviews[index]]; UIView *fitestView = [self.subviews[index] hitTest:childViewPoint withEvent:event]; if (fitestView) { return fitestView; } return nil; } return self; } ``` 给出 一个Demo地址/uff1a[https://github.com/FantasticLBP/BlogDemos/tree/master/模仿系统找出事件的最佳响应者](https://github.com/FantasticLBP/BlogDemos/tree/master/模仿系统找出事件的最佳响应者 "模仿系统找出事件的最佳响应者") 实验/uff1a 在控制器/uff08ViewController/uff09的view上先添加一个UIButton/uff0c再添加一个自定义的UIView/(ShelterView/),盖在button的上面。 需求/uff1a点击ShelterView上的点/uff0c如果点也在UIButton范围上则交给UIButton处理事件/uff0c如果不在UIButton上则交给ShelterView处理/uff0c如果点击屏幕上除了ShelterView之外的点则交给控制器的view处理。 ``` //ViewController -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"viewController->%s",__func__); } //ShelterView #import "ShelterView.h" @implementation ShelterView -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"%s",__func__); } -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ NSLog(@"%s",__func__); /** 需求/uff1a不管点击按钮还是view都交给button处理 思路/uff1a在view的hitTest方法中寻找最合适的view/uff0c那么返回nil告诉系统view不是最合适的view/uff0c那么系统则认为按钮是最合适的view return nil; */ //需求/uff0c在view上点击/uff0c如果点击范围在button上则由button进行处理事件/uff1b否则交给view处理事件 UIView *button = nil; for (UIView *subView in self.superview.subviews) { //判断事件的点是否在按钮上 if ([subView isKindOfClass:[UIButton class]]) { button =subView; } CGPoint btnPoint = [self convertPoint:point toView:button]; if ([button pointInside:btnPoint withEvent:event]) { return button; }else{ //此时代表事件触摸点不在button上/uff0c但是也不能写nil/uff0c写nil的话点击屏幕上的其他地方系统会寻找最合适的view/uff0c此时返回nil/uff08 return nil;/uff09/uff0c则代表view不是最合适的view,那么此时点击屏幕上除了按钮之外的区域/uff0c最合适的view就是控制器上面的view return [super hitTest:point withEvent:event]; } } return nil; } @end ``` 要看完整Demo/uff0c地址为/uff1a[https://github.com/FantasticLBP/BlogDemos/tree/master/hitTest的神奇效果/uff08一/uff09](https://github.com/FantasticLBP/BlogDemos/tree/master/hitTest的神奇效果/uff08一/uff09 "hitTest的神奇效果")