事件传递机制
在自定义控件中,可能需要动态响应事件,如按钮太小,不易点击,需要扩大按钮的点击范围,接下来我们谈谈iOS的事件传递机制。
事件响应链
UIResponder类能够响应触摸、手势以及远程控制等事件。它是所有可响应事件的基类,其中包括很常见的UIView、UIViewController以及UIApplication。
UIResponder的属性和方法如下图,其中nextResponder表示指向一个UIResponder对象。
那么事件响应链与UIResponder有什么关系呢?应用内的视图按一定的结构组织起来,即树状层次结构,一个视图可以有多个子视图,而子视图只能有一个父视图。当一个视图被添加到父视图上时。每一个视图的nextResponder属性就指向它的父视图,这样,整个应用就通过nextResponder串成了一条链,即响应链。响应链是一个虚拟链,并不是真实存在的,它借助UIResponder的nextResponder串连起来。如下图
Hit-Test View
有了事件响应链,接下来就是寻找具体响应对象了,我们称之为:Hit-Testing View,寻找这个View的过程称为Hit-Test。
什么是Hit-Test?我们可以把它理解为一个探测器,通过这个探测器,我们可以找到并判断手指是否触摸在某个视图上。
Hit-Test是如何工作的?Hit-Test采用递归方式从视图的根节点开始遍历,直到找到某个点击的视图。
首先从UIWindow发送hitTest:withEvent:消息开始,判断该视图是否能响应触摸事件,如果不能响应返回nil,表示该视图不能响应触摸事件。然后再调用pointInside:withEvent:方法,该方法用于判断触摸事件点击的位置是否处理该视图范围内,如果pointInside:withEvent:返回no,那么hitTest:withEvent:也直接返回nil。
如果pointInside:withEvent: 方法返回yes,那么该视图向所有子视图发送hitTest:withEvent:消息,所有子视图的调用顺序是从最顶层视图一直到最底层视图,即从subViews的数组的末尾向前遍历。直到有子视图返回非空对象或全部遍历完毕。若有子视图返回非空对象,则hitTest:withEvent:方法返回该对象,处理结束;若所有子视图都返回nil,则hitTest:withEvent:方法返回该视图自身。
事件传递机制的应用
举几个例子,说明一下事件传递机制在自定义控件中的应用。
一、扩大view的点击区域。假设一个button的大小为20px 20px,太小难以点击。我们通过重写这个button子类的hitTest:withEvent:方法,判断点击处point是否在button周围20px以内,如果是则返回自身,实现扩大点击范围的功能,代码如下:










