在iOS中事件分为三类:
- 触摸事件:通过触摸、手势进行触发(点击,缩放等)
- 运动事件:通过加速器进行触发(微信摇一摇等)
- 远程控制事件:通过其他远程设备触发(线控耳机)
只有继承自UIResponder的类才能处理事件
下面说一下触摸事件
当用户触摸屏幕时,事件会被封装成一个event实例,包含了触摸相关信息,然后操作系统对其进行分发,由响应者类进行处理(UIResponder的子类);
简单的分发与响应过程图:
1.事件的分发
iOS系统检测到手指触摸(Touch)操作时会将其放入当前活动Application的事件队列,UIApplication会从事件队列中取出触摸事件并传递给key window(当前接收用户事件的窗口)处理,window对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,称之为hit-test view。
window对象会在首先在view hierarchy的顶级view上调用hitTest:withEvent:,此方法会在视图层级结构中的每个视图上调用pointInside:withEvent:,如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是hit-test view。
hitTest:withEvent:方法的处理流程如下:
- 首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
- 若返回NO,则hitTest:withEvent:返回nil;
- 若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
- 若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
- 如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)
简单来说,通过hitTest:withEvent:和pointInside:withEvent:这两个方法找到触摸对象(触摸对象并不一定就是响应者)
2.响应者链
简单来说响应者链就是一些拥有当前事件处理权力的实例对象组成的链条(如理解有误请指出)
响应者链上的对象 1.处理事件(处理事件之后事件将不继续向下一响应者传递,可以通过复写方法把当前事件交给某一对象进行处理)
2.不处理(如果所有响应者都不处理事件,最终事件被丢弃)
可以复写下列方法对事件进行处理
//触摸开始,手指触碰屏幕 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event //触摸结束,手指离开屏幕 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event //触摸取消(如电话接入的时候) - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event //手指移动(会调用多次) - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event //3D touch 9.1之后加入的3D触摸事件 - (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches
需要注意的是:UIView有几种情况者不能处理事件
1.userInteractionEnabled=NO (UIImageView的userInteractionEnabled 默认就是NO)
2.hidden=YES
3.alpha小于0.01
3.开发过程中也会碰到这样的需求:
例如触摸一个UIView实例对象,要将页面进行跳转,一般这个功能是由视图(UIViewController)控制器来完成的,
这样我们就需要找到当前视图的视图控制器对事件进行处理,方法如下
- (UIViewController *)viewController { UIResponder *responder = self.nextResponder; do { if([responder isKindOfClass:[UIViewController class]]) { return (UIViewController *)responder; } responder = responder.nextResponder; }while (responder != nil); return nil; }
需要其他对象对事件进行处理方法类似(只要这个对象在响应者链上)