iOS事件的响应者链
iOS 事件响应者链
1 iOS中的事件
触摸事件 加速计事件 远程控制事件
在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。以下都是继承自UIResponder的,所以都能接收并处理事件。
UIApplication
UIViewController
UIView
那么为什么继承自UIResponder的类就能够接收并处理事件呢?
UIResponder内部提供了以下方法来处理事件触摸事件
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event;
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event;
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event;
加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent*)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent*)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent*)event;
远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent*)event;
2 事件的处理
// UIView是UIResponder的子类,可以覆盖下列4个方法处理不同的触摸事件
// 一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesBegan:(NSSet *)toucheswithEvent:(UIEvent *)event
// 一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesMoved:(NSSet *)toucheswithEvent:(UIEvent *)event
// 一根或者多根手指离开view,系统会自动调用view的下面方法
- (void)touchesEnded:(NSSet *)toucheswithEvent:(UIEvent *)event
// 触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
- (void)touchesCancelled:(NSSet *)toucheswithEvent:(UIEvent *)event
// 提示:touches中存放的都是UITouch对象
3 事件的产生和传递
3.1 事件的产生
3.1.1 发生触摸事件后,系统会将该事件加入到UIApplication管理的事件队列中(队列是先进先出,栈是先进后出)。
3.1.2 UIApplication从事件队列中取出最前面的事件,并将事件分发下去已便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
3.1.3 主窗口在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
3.1.4 找到合适的视图控件后,就会调用视图控件的touches方法来做具体的事件处理。
3.2 事件的传递
3.2.1 触摸事件的传递是从父控件传递到子控件(UIApplication —> window -> 寻找处理事件最合适的View)。
3.2.2 首先判断主窗口(keyWindow)自己是否能接受触摸事件。
3.2.3 判断触摸点是否在自己身上。
3.2.3 子控件数组中从后往前遍历子控件,重复前面的两个步骤(从最上层的View开始)。
3.2.4 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。
3.3 寻找最适合的View的底层
//作用:寻找最适合的View
//什么时候调用:只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *fitView = [super hitTest:point withEvent:event];
NSLog(@"%@",fitView); return fitView;
}
//作用:判断触摸点在不在当前的View上.
//什么时候调用:在hitTest方法当中会自动调用这个方法.
//注意:point必须得要跟当前View同一个坐标系.
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return YES;
}
hitTest: withEvent:的底层实现原理
// 判断自己能否接收事件
if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01){
return nil;
}
// 触摸点在不在自己身上
if ([self pointInside:point withEvent:event] == NO) {
return nil;
}
// 从后往前遍历自己的子控件(重复前面的两个步骤)
int count = (int)self.subviews.count; for (int i = count -1; i >= 0; i--) {
UIView *childV = self.subviews[i];
// point必须得要跟childV相同的坐标系.
// 把point转换childV坐标系上面的点
CGPoint childP = [self convertPoint:point toView:childV];
UIView *fitView = [childV hitTest:childP withEvent:event];
if (fitView) {
return fitView;
}
}
// 如果没有符合条件的子控件,那么就自己最适合处理
return self;
UIView不能接收触摸事件的三种情况1.不允许用户交互userInteractionEnabled = NO。2.当一个控件隐藏时Hidden = YES(如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接收触摸事件)。3.当一个控件为透明时。