# 事件响应者链 实验1: 定义 BaseView,在里面实现方法touchBegan,监听当前哪个类调用了该方法。 在控制器的界面上加5个颜色不同的view,每个view自定义view去实现,因此在不同的view上的手势就可以由不同的view拦截到。 ![UI效果图](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/Simulator%20Screen%20Shot%20-%20iPhone%206s%20Plus%20-%202017-10-11%20at%2010.14.37.png) ```objective-c //BaseView #import "BaseView.h" @implementation BaseView -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"%@",[self class]); } ``` 结果:点击不同的View打印出不同的类名。 结论: * 触摸事件是从父控件传递到子控件的。 * 点击了绿色(图上的2级)的view:UIApplication-> UIWindow -> UIViewController的view -> 绿色的view * 点击了蓝色(图上的3级)的view:UIApplication-> UIWindow -> UIViewController的view -> 红棕色的view -> 蓝色的view * 点击了黄色(图上的4级)的view:UIApplication -> UIWindow -> UIViewController的view -> 红棕色的view -> 蓝色的view -> 黄色的view 注意:如果父控件不能接收触摸事件,那么这个父控件的子控件也不能接收触摸事件 #### 如何找到最合适的控件来接收触摸事件? * 自己能否接收触摸事件? * 触摸点是否在自己身上? * 从后往前遍历子控件,重复前面2个步骤 * 如果没有符合条件的子控件,那么就自己最适合处理 # 事件响应原理 产生的touch方法的默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者处理。 #### 响应者链条 ![响应者链条](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/reponseChain.png) #### 事件传递的完整过程 1. 先将事件对象由上往下传递(父控件传递给子控件),找到最合适的控件来处理 2. 调用最合适控件的touch方法 3. 如果调用了\[super touches...\]方法就会将事件顺着响应者链条向上传递,传递给上一个响应者 4. 接着就会调用上一个响应者的touches...方法 #### 事件响应者 ##### 如何判断该控件的上一个响应者? 1. 如果当前这个view是控制器的view,那么上一个响应者就是控制器 2. 如果当前这个view不是控制器的view,那么上一个响应者就是父控件。 事件传递给UIApplication后如果不处理的话,该事件会销毁掉。 控制器view上的子控件的touch...方法如果子控件不处理那么都会顺着响应者链条向上传递给上一层响应者对象,比如可以交给控制器处理。 ## 手饰事件和点击事件的响应顺序 假如给某个 view 所在的父视图添加了手饰识别器。 **手势识别器的优先级**:如果你将 `UITapGestureRecognizer` 添加到了视图上,UIKit 会首先尝试识别手势。如果视图上添加了多个手势识别器,它们的识别顺序将根据它们被添加到视图的顺序或者它们的 `delaysTouchesBegan` 和 `delaysTouchesEnded` 属性来决定。 想要子 view 响应事件而不是被根视图拦截,则需要给手势识别器添加代理,实现代理方法 ```objective-c UIGestureRecognizer *gesture; gesture.delegate = self; - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return NO; } ``` 如果 `UITapGestureRecognizer` 识别了一个手势,它可以通过设置 `cancelsTouchesInView` 属性为 `YES` 来取消视图上的触摸事件,这样点击事件就不会被进一步传递到视图控制器的 `touchesBegan` 或 `touchesEnded` 方法。