在iOS 开发中,常见的事件有三种类型,分别是:
(1)触摸事件:平常手指在屏幕上滑动,产生的事件都是触摸事件
(2)加速计事件:微信的摇一摇就是典型的加速计事件
(3)远程控制事件:耳机控制歌曲上一首、下一首、暂停就是远程控制事件的应用。
在触摸事件中,通常情况下,点击哪个控件,哪个控件就会产生反应。比如说,点击确定按钮,确定按钮会响应该事件,点击取消按钮,取消按钮会响应该事件。那么,系统是如何决定哪一个视图(控件)来响应该事件呢?
当发生触摸事件后,系统会将该事件加入到一个由 UIApplication 管理的队列中,UIApplication 会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口。主窗口会在整个视图层次结构中找到一个 最合适 的视图来处理该触摸事件。找到合适的视图控件后,就会调用该控件响应的方法来处理触摸事件,常用的方法有:
touchesBegan...
touchesMoved...
touchesEnded...
touchesCancelled...
在app 开发中,一个控制器下通常不止一个控件,那么发生触摸事件后,哪一个控件是最合适处理该事件的呢?
在确定最合适控件的过程中,遵循以下原则:
(1)判断自己能否接收触摸事件,如果不能,直接返回;如果能,到第(2)步
(2)判断触摸点是否在自己身上,如果不在,直接返回;如果在,到第(3)步
(3)从后往前遍历子控件,重复前两步
(4)如果没有符合条件的子控件,那么自己就是最适合处理该触摸事件的控件。
举例如下:
其中,绿色view和橙色 view都是白色view的子控件(假设先加入了绿色view,后加入了橙色view),蓝色view和红色view都是橙色view的子控件,黄色view是蓝色view的子控件。
当点击绿色view时,事件先传递到白色view。白色view满足条件(1),到步骤2,触摸点在自己身上,到步骤3,对橙色view做步骤(1)和步骤(2),不满足步骤(2),回退,对绿色view做步骤(1)和步骤(2),均满足,到步骤(3),没有子控件,到步骤(4),自己就是最合适的控件(绿色view)。整个的流程如下:
白色view --->橙色view(橙色view不满足步骤2,因此遍历绿色view)
---->绿色view(满足步骤1和步骤2),没有子控件,所以绿色view是最适合处理该触摸事件的控件。
当点击黄色view时,流程如下:
白色view --->橙色view --->红色view(红色view覆盖了蓝色view,可知红色view是后加入的。红色view不满足条件2,因此到蓝色view)
--->蓝色view --->黄色view(黄色view没有子控件,所以黄色view是最适合处理该触摸事件的控件)。
也就是说,在寻找最合适的子视图时,是从父视图控件开始,一层一层向下寻找的。
在找到最合适的视图控件后,就会调用控件的 touchesBegan... 等方法。 这些touches的默认做法是将事件顺着响应者链条向上传递,交给上一个响应者处理 。
假设点击了上图中的黄色控件,如果黄色控件不处理该触摸事件,会将该事件交给蓝色控件处理;如果蓝色控件不处理该控件,会将该控件交给橙色控件处理;如果橙色控件不处理该事件,会将该事件交给白色控件来处理;如果白色控件不处理该事件,白色控件会交给控制器来处理;如果控制器不处理该事件,控制器会交给主窗口处理该事件;如果主窗口不处理该事件,主窗口会交给UIApplication来处理该事件;如果UIApplication也不处理该事件,则该事件会被丢弃,不做处理。因此说,事件的处理过程是顺着响应者链条向上传递的。
示意图如下:
左侧是单控制器,右侧是多控制器。
响应者链的事件传递过程总结如下:
1.如果view的控制器存在,就传递给控制器处理;如果控制器不存在,则传递给它的父视图
2.在视图层次结构的最鼎城,如果也不能处理收到的事件,则将事件传递给window对象进行处理
3.如果window对象也不处理,则将事件传递给 UIApplication对象
4.如果UIApplication也不能处理该事件,则将该事件丢弃。
上传一个验证响应者链的代码(MJ所写)