响应者对象
响应者对象(Response object) 响应者对象就是可以响应事件并对事件作出处理。iOS中UIResponder类,定义了响应者对象的所有方法。UIApplication、UIView等类都继承了UIResponder类,UIWindow和UIKit框架下的控件,因为继承了UIView,所以也属于响应者类
第一响应者
当前接受触摸的响应者对象,它是响应者链的开端。事件首先被发送给第一响应者,第一响应者往往是事件发生的视图,也就是用户触摸屏幕的地方。事件将沿着响应者链一直向下传递,直到被接受并做出处理。
响应者链
响应者链表示一系列的响应者对象。事件交由第一响应者对象处理,如果第一响应者对象不处理,事件被沿着响应者链向上传递,交给下一个响应者(next responder)。
事件传递的过程
- 先将事件对象由上往下传递(由父控件传递给子控件),找到最合适的控件来处理这个事件。 调用最合适控件的touches….方法,如果调用了[super touches….];就会将事件顺着响应者链往上传递,传递给上一个响应者对象,接着就会调用上一个响应者对象的touches….方法。
- iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。
- UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,此方法会在其视图层级结构中的每个视图上调用pointInside:withEvent:(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是要找的hit-test view。首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;若返回NO,则hitTest:withEvent:返回nil;若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。
响应者链的传递过程
- 正常的响应者链流程经常被委托(delegation)打断,一个对象(通常是视图)可能将响应工作委托给另一个对象来完成(通常是视图控制器ViewController),这就是为什么做事件响应时在ViewController中必须实现相应协议来实现事件委托。先将事件对象由下往上传递,由父控件传递给子控件,找到最合适的控件来处理这个事件(调用touches...方法)。如果调用了super的touches方法,就会将事件顺着响应者链条往上传递,传递给上一个响应者的touches方法。
- 第一响应者——> 第一响应者的视图控制器(如果有)——>父容器(如果有)——>父容器的视图控制器(如果有)——>UIWindow——>UIApplication——>应用程序委托对象
相应的线路图如下:
First Responser -- > The Window -- >The Application -- > App Delegate
不能响应的几种情况
- 不接受交互
- 隐藏
- 透明度为0-0.01
发生触摸事件后,系统会将该事件加入UIApplication管理的事件队列中。UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序主窗口。触摸事件通过父控件传递到子控件。
例如有1->2->3->4->5这么几层View,点击事件需要通过UIApplication传给UIWindow,然后向子控件传递。
如果父控件拒绝接收事件,那么点击子控件是无效的。
如果传递链在中间断开,则会交给断点前的控制器处理。
例如3->4处损坏,点击5会由3处理,如果5拒绝接收,会由4处理。
图片默认的userInteractionEnabled为NO,默认不接收事件,因此不会遮盖下面的视图接收触摸事件。
以后发现按钮不管用,检查父控件是否userInterationEnabled为YES,例如图片上的按钮。
imageView的响应为YES,会把事件回抛到父控件(响应者链条)。
事件顺着响应者链条向上传递,找到最合适的控件调用touches....方法。
可以实现事件的回传。
1->2->3 三层
如果要实现2、3都监听触摸,调用3的super的touches...方法即可。
补充:如果经过上面的传播过程,还没有被处理,那么该事件就会被丢弃。
(从网上看了一些自己总结整理了一些,不是很全有待补充)