UIResponder
是 iOS 中用于解决用户事件的 API,能够解决触摸事件、按压事件 (3D touch)
、近程管制事件、硬件静止事件。 能够通过 touchesBegan
、pressesBegan
、motionBegan
、remoteControlReceivedWithEvent
等办法,获取到对应的回调音讯。
_**UIResponder**
不只用来接管事件,还能够解决和传递对应的事件,_如果以后响应者不能解决,则转发给其余适合的响应者解决。
应用程序通过响应者来接管和处理事件,响应者能够是继承自 UIResponder
的任何子类,例如 UIView
、UIViewController
、UIApplication
等。当事件来到时,零碎会将事件传递给适合的响应者,并且将其成为第一响应者。
第一响应者未解决的事件,将会在响应者链中进行传递,传递规定由 UIResponder
的nextResponder
决定,能够通过重写该属性来决定传递规定。当一个事件到来时,第一响应者没有接管音讯,则顺着响应者链向后传递。
查找第一响应者
根底 API
查找第一响应者时,有两个十分要害的 API
,查找第一响应者就是通过一直调用子视图的这两个API
实现的。
调用办法,获取到被点击的视图,也就是第一响应者。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
hitTest:withEvent: 办法外部会通过调用这个办法,来判断点击区域是否在视图上,是则返回YES
,不是则返回NO
。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
查找第一响应者
应用程序接管到事件后,将事件交给 keyWindow
并转发给根视图,根视图依照视图层级逐级遍历子视图,并且遍历的过程中一直判断视图范畴,并最终找到第一响应者。
从 keyWindow
开始,向前逐级遍历子视图,一直调用 UIView
的hitTest:withEvent:
办法,通过该办法查找在点击区域中的视图后,并持续调用返回视图的子视图的 hitTest:withEvent:
办法,以此类推。如果子视图不在点击区域或没有子视图,则以后视图就是第一响应者。
在 hitTest:withEvent:
办法中,会从上到下遍历子视图,并调用 subViews
的pointInside:withEvent:
办法,来找到点击区域内且最下面的子视图。如果找到子视图则调用其 hitTest:withEvent:
办法,并继续执行这个流程,以此类推。如果子视图不在点击区域内,则疏忽这个视图及其子视图,持续遍历其余视图。
能够通过重写对应的办法,管制这个遍历过程。通过重写 pointInside:withEvent:
办法,来做本人的判断并返回 YES
或NO
,返回点击区域是否在视图上。通过重写 hitTest:withEvent:
办法,返回被点击的视图。
此办法在遍历视图时,疏忽以下三种状况的视图,如果视图具备以下特色则疏忽。然而视图的背景色彩是clearColor
,并不在疏忽范畴内。
- 视图的
hidden
等于 YES。 - 视图的
alpha
小于等于 0.01。 - 视图的
userInteractionEnabled
为 NO。
如果点击事件是产生在视图外,但在其子视图外部,子视图也不能接管事件并成为第一响应者。这是因为在其父视图进行 hitTest:withEvent:
的过程中,就会将其疏忽掉。
作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是一个我的 iOS 交换群:642363427 不论你是小白还是大牛欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,大家一起交流学习成长!
事件传递
传递过程
UIApplication
接管到事件,将事件传递给keyWindow
。keyWindow
遍历subViews
的hitTest:withEvent:
办法,找到点击区域内适合的视图来处理事件。UIView
的子视图也会遍历其subViews
的hitTest:withEvent:
办法,以此类推。- 直到找到点击区域内,且处于最上方的视图,将视图逐渐返回给
UIApplication
。 - 在查找第一响应者的过程中,曾经造成了一个响应者链。
- 应用程序会先调用第一响应者处理事件。
- 如果第一响应者不能处理事件,则调用其
nextResponder
办法,始终找响应者链中能解决该事件的对象。 - 最初到
UIApplication
后依然没有能解决该事件的对象,则该事件被废除。