大多数事件的分发都是依赖响应链的。
响应链
是由一系列连接在一块儿的响应者
(UIResponse子类:UIApplication
、UIViewController
、UIView
)组成的。通常状况下,一条响应链开始于第一响应者,结束于application对象。若是一个响应者不能处理事件,则会将事件沿着响应链传到下一响应者。ios
@property(nonatomic, readonly, nullable) UIResponder *nextResponder;
复制代码
事件被苹果分为3种大类型: 触摸事件,加速计事件以及远程遥控事件git
当一个事件发生后,事件会从父控件传给子控件,也就是说由github
硬件
->系统
->UIApplication
->UIWindow
->SuperView
->SubView
bash以上就是事件的传递,也就是寻找
第一响应者
的过程。 符合第一响应者
的条件包括:app
- touch事件的位置在响应者区域内
pointInside:withEvent: == YES
- 响应者
self.hidden != NO
- 响应者
self.alpha > 0.01
- 响应者
self.userInteractionEnabled = YES
- 遍历 subview 时,是从上往下顺序遍历的,即 view.subviews 的 lastObject 到 firstObject 的顺序,找到合适的响应者view,即中止遍历.
第一响应者
对于接收到的事件有3种操做:ide
- 不拦截,默认操做。事件会自动沿着默认的响应链往下传递
- 拦截,再也不往下分发事件。重写
touchesBegan:withEvent:
进行事件处理,不调用父类的touchesBegan:withEvent:
- 拦截,继续往下分发事件。重写
touchesBegan:withEvent:
进行事件处理,同时调用父类的touchesBegan:withEvent:
将事件往下传递
下图展现了Hit-Testing的逻辑ui
UIView
是做为UIViewController
根视图存在的,则其nextResponder
为UIViewController
对象;UIWindow
上的,则其nextResponder
为UIWindow对象。// 若触摸发生在UITextField上,则事件的传递顺序是:
UITextField ——> UIView ——> UIView ——> UIViewController ——> UIWindow ——> UIApplication ——> UIApplicationDelegation
复制代码
superview
superview
也尝试来处理事件,若是他处理不了,继续传递他的父视图 UIViewcontroller.view
UIViewController.view
尝试来处理该事件,若是处理不了,将把该事件传递给 UIViewController
UIViewController
尝试处理该事件,若是处理不了,将把该事件传递给主窗口 Window
Window
尝试来处理该事件,若是处理不了,将传递给应用单例 Application
Application
也处理不了,则该事件将会被丢弃事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。atom
若是当前这个view是控制器的view,那么控制器就是上一个响应者 若是当前这个view不是控制器的view,那么父控件就是上一个响应者spa
若是view
的控制器存在,就传递给控制器;若是控制器不存在,则将其传递给它的父视图 在视图层次结构的最顶级视图,若是也不能处理收到的事件或消息,则其将事件或消息传递给 window
对象进行处理 若是 window 对象也不处理,则其将事件或消息传递给 UIApplication
对象 若是 UIApplication
也不能处理该事件或消息,则将其丢弃(销毁)code
由于系统默认作法是把事件上抛给父控件,因此能够经过重写本身的touches方法和父控件的touches方法来达到一个事件多个对象处理的目的。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.本身先处理事件...
NSLog(@"do somthing...");
// 2.再调用系统的默认作法,再把事件交给上一个响应者处理
[super touchesBegan:touches withEvent:event];
}
复制代码
重写子view的point:inside`` →
扩大Button的点击区域(上下左右各增长20)重写父view的point:insde`` →
子view超出了父view的bounds响应事件userInteractionEnabled = NO
,结果是包括父控件在内的全部子控件都不能处理触摸事件(虽然设置透明度和hidden=YES
也能够,可是那样就看不见了注意:若是父控件的透明度设置为0或者hidden=YES,那么子控件也是不可见的。)isUserInteractionEnabled=false
,想让它继续继续处理触摸事件,能够在它的父控件的hitTest方法中直接返回它。point:inside:
方法point:inside:
方法point:inside:
方法返回false,则对应的hitTest返回nilpoint:inside:
方法返回true,则调用对应的hitTest方法重复上面的操做返回子控件的最合适子控件UIGestureRecongnizer
、UIContorl
均可以处理触摸事件UIGestureRecognizer
:使用addGestureRecognizer
方法处理事件UIControl
:使用addTarget
方法处理事件UIResponder
:使用touches
等一系列方法处理事件
UIButton继承自UIControl
,UIControl继承自UIView
,若是给UIButton
添加了手势,并实现了本身的处理事件的>>方法,当点击UIButton
的时候发现touches
方法走了,手势方法(addGestureRecognizer
)也走了,本身的方法(addTarget
)没有走。由此能够得出一个结论:
UIGestureRecognizer的优先级
> >UIContol的优先级
,当一个UIButton即实现了本身的方法(addTarget
),又添加了手势方法(addGestureRecognizer
)的话,本身的方法(addTarget
)会被屏蔽掉,不论是否添加了手势,touches
方法都会处理。