事件类型
在iOS系统中,一共有三种形式的事件:触摸事件(Touch Event),运动事件(Motion Event)和远端控制事件(Remote-control Event)。顾名思义,触摸事件就是当用户触摸屏幕时发生的事件,而运动事件是用户移动设备时发生的事件:加速计,重力感应。远端控制事件可能比较陌生:如通过耳机进行控制iOS设备声音等都属于远端控制事件。
iOS处理触屏事件,分为两种方式。高级事件处理,利用UIKit提供的各种用户控件或者手势识别器来处理事件。低级事件处理,在UIView的子类中重写触屏回调方法,直接处理触屏事件。
触摸事件
UIEvent: iOS将触摸事件定义为第一个手指开始触摸屏幕到最后一个手指离开屏幕定义为一个触摸事件。用类UIEvent表示。
UIEvent的作用:一次完整的触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数,如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象,如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象,根据touches中UITouch的个数可以判断出是单点触摸还是多点触摸。
UITouch: 一个手指第一次点击屏,会形成一个UITouch对象,直到离开销毁。表示触碰。UITouch对象能表明了当前手指触碰的屏幕位置,状态。状态分为开始触碰、移动、离开。
UITouch的作用: 当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象,一根手指对应一个UITouch对象,保存着跟手指相关的信息,比如触摸的位置、时间、阶段,当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置,当手指离开屏幕时,系统会销毁相应的UITouch对象。
在屏幕上的每一次动作事件都是一次Touch,在iOS中用UITouch对象表示每一次的触控,多个Touch组成一次Event,用UIEvent来表示一次事件对象。UIEvent实际包括了多个UITouch对象。有几个手指触碰,就会有几个UITouch对象。
UIView的触摸事件函数
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event; // 一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event; // 一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event; // 一根或者多根手指离开view,系统会自动调用view的下面方法
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event; // 触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
提示:touches中存放的都是UITouch对象
响应链
响应者对象(UIResponder)
UIResponser就是用来接收和处理事件的类,先抛开iOS中的具体传递细节,系统发送UIEvent的Touch message给UIResponser类。UIResponser提供了一下几个函数来做事件处理
//触摸事件- (void)touchesBegan:(NSSet *)touches withEvent:(nullableUIEvent*)event;- (void)touchesMoved:(NSSet *)touches withEvent:(nullableUIEvent*)event;- (void)touchesEnded:(NSSet *)touches withEvent:(nullableUIEvent*)event;- (void)touchesCancelled:(NSSet *)touches withEvent:(nullableUIEvent*)event;- (void)touchesEstimatedPropertiesUpdated:(NSSet *)touchesNS_AVAILABLE_IOS(9_1);//物理按钮,遥控器上面的按钮在按压状态等状态下的回调- (void)pressesBegan:(NSSet *)presses withEvent:(nullableUIPressesEvent*)eventNS_AVAILABLE_IOS(9_0);- (void)pressesChanged:(NSSet *)presses withEvent:(nullableUIPressesEvent*)eventNS_AVAILABLE_IOS(9_0);- (void)pressesEnded:(NSSet *)presses withEvent:(nullableUIPressesEvent*)eventNS_AVAILABLE_IOS(9_0);- (void)pressesCancelled:(NSSet *)presses withEvent:(nullableUIPressesEvent*)eventNS_AVAILABLE_IOS(9_0);//设备的陀螺仪和加速传感器使用- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullableUIEvent*)eventNS_AVAILABLE_IOS(3_0);- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullableUIEvent*)eventNS_AVAILABLE_IOS(3_0);- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullableUIEvent*)eventNS_AVAILABLE_IOS(3_0);
响应链
响应链是“事件派发”的原则和规定,那么响应链是什么?顾名思义事件链是一个链条,详细的定义如下: 每条链是一个 链表状结构,整个是一棵树,链表的每一个node是一个 UIResponser对象。
事件响应链的形成和事件的响应和传递,UIResponder都帮我们做了很多事。我们的app中,所有的视图都是按照一定的结构组织起来的,即树状层次结构,每个view都有自己的superView,包括controller的topmost view(controller的self.view)。当一个view被add到superView上的时候,他的nextResponder属性就会被指向它的superView,当controller被初始化的时候,self.view(topmost view)的nextResponder会被指向所在的controller,而controller的nextResponder会被指向self.view的superView,这样,整个app就通过nextResponder串成了一条链,也就是我们所说的响应链。所以响应链就是一条虚拟的链,并没有一个对象来专门存储这样的一条链,而是通过UIResponder的属性串连起来的。如下图:
当一个事件发生时,如果 first responder 不处理,事件就会继续往下传递,被下个 responder 接收,如果下个 responder 也不处理,又会被下下个 responder 接收…… 直到一个 responder 处理了事件或者没有 responder 了。这些 responder 按照传递次序连接起来的链条就构成了响应者链。
由于不同的 app 内的布局和层次结构的不同,响应顺序也会有所不同,但事件的传递路径会遵守基本规则。从图中可以看到,响应者链有以下特点:
响应者链通常是由 initial view 开始;
UIView 的 nextResponder 它的 superview;如果 UIView 已经是其所在的UIViewController 的 top view,那么 UIView 的 nextResponder 就是 UIViewController;
UIViewController 如果有 Super ViewController,那么它的 nextResponder 为其 Super ViewController 最表层的 View;如果没有,那么它的 nextResponder 就是 UIWindow;
UIWindow 的 contentView 指向 UIApplication,将其作为 nextResponder;
UIApplication 是一个响应者链的终点,它的 nextResponder 指向nil,整个 responder chain 结束。
需要说明是,如果当前的 responder 不处理事件,并希望将其传递给 nextResponder 时,需要手动编写代码,才会继续往下传递,否则事件会被废弃。
总结
所以关于事件的链有两条:事件的响应链;Hit-Testing 时事件的传递链。
响应链:由离用户最近的view向系统传递。
initial view –> super view –> …..–> view controller –> window –> Application –> AppDelegate
Hit-Testing 链:由系统向离用户最近的view传递。
UIKit –> active app's event queue –> window –> root view –>……–>lowest view
借鉴以下文章
史上最详细的iOS之事件的传递和响应机制-原理篇 iOS触摸事件处理详解 事件处理 深入浅出iOS事件机制 iOS 点击事件传递及响应 iOS Events and Responder Chain