響應(yīng)者鏈顧名思義就是由一系列能夠響應(yīng)事件的響應(yīng)者對(duì)象組成的一個(gè)層式結(jié)構(gòu)。我們把具有響應(yīng)和處理事件能力的對(duì)象稱為響應(yīng)者對(duì)象。
事件:有三種
1、Touch Events,點(diǎn)擊事件;
2、Motion Events,移動(dòng)事件,比如監(jiān)聽加速器、陀螺儀 產(chǎn)生的事件;
3、Remote Control Events,遠(yuǎn)程控制事件, 比如耳機(jī),可以控制你的音量、播放音樂。響應(yīng)對(duì)象
UIResponder 是所有具有響應(yīng)對(duì)象的基類,我們熟悉的UIApplication、UIViewController、UIWindow 和所有繼承自UIView的 UIKit 都直接或間接的繼承自 UIResponder,所以它們的實(shí)例都是可以構(gòu)成響應(yīng)者鏈的響應(yīng)對(duì)象。
當(dāng)用戶點(diǎn)擊屏幕的時(shí)候,觸摸事件通過(guò) hitTest:withEvent: 來(lái)確定first Response,該方法接收參數(shù)CGPoint 和 UIEvent,并從底層開始按照subView的順序測(cè)試該CGPoint在哪個(gè)View上,如果在該View上,則繼續(xù)測(cè)試是否在View的subview上。
舉個(gè)??

假設(shè)用戶觸摸了圖中的view E。iOS通過(guò)如下順序查找hit-test view
1.觸摸點(diǎn)在A里面,因此檢測(cè)子view B和C
2.觸摸點(diǎn)不在B里面,但是在C里面。因此檢測(cè)C的子View D和E。
3.觸摸點(diǎn)不在D里面,但是在E里面,并且E是在最外層的包含觸摸點(diǎn)的view,因此E就是要找的hit-test view
hitTest:withEvent:函數(shù)的實(shí)現(xiàn)代碼:
1.能否自己處理?不能,return nil;
2.點(diǎn)在不在當(dāng)前控件上?沒在,return nil;
3.說(shuō)明能處理觸摸事件,并且在當(dāng)前控件上,是合適的控件,但不一定是最合適的。從后往前遍歷自己的子控件,是否是最合適的控件(包含該觸摸點(diǎn)的View)。如果是,返回該View。
4.說(shuō)明沒找到比自己合適的View,返回自己。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event的實(shí)現(xiàn):
[objc] view plain copy
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01)
{
return nil;
}
if (![self pointInside:point withEvent:event])
{
return nil;
}
__block UIView *hitView = self;
[self.subViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOLBOOL *stop) {
CGPoint thePoint = [self convertPoint:point toView:obj];
UIView *theSubHitView = [obj hitTest:thePoint withEvent:event];
if (theSubHitView != nil)
{
hitView = theSubHitView;
*stop = YES;
}
}];
return hitView;
}
- 視圖允許接收觸摸事件的條件是:
視圖不是隱藏的:self.hidden == NO
視圖是允許交互的:self.userInteractionEnabled ==true
視圖透明度大于0.01:self.alpha > 0.01
視圖包含這個(gè)點(diǎn): pointInside:withEvent: ==true
hit-test view和響應(yīng)鏈的概念:當(dāng)一個(gè)事件發(fā)生需要處理時(shí),會(huì)讓合適的對(duì)象去處理。如果是觸摸事件的話,該對(duì)象就是hit-test view。如果是其他事件,該對(duì)象指的就是第一響應(yīng)者(響應(yīng)鏈中)。響應(yīng)鏈?zhǔn)且粋€(gè)比較大的范疇,在觸摸事件中,hit-test view就是響應(yīng)鏈中的第一響應(yīng)者。也就是說(shuō)在觸摸事件中通過(guò)hitTest:withEvent:方法找到的hit-test view就是第一響應(yīng)者。
下圖給出了沿著響應(yīng)鏈傳遞的順序。兩個(gè)圖的區(qū)別是視圖的層次關(guān)系不一樣。響應(yīng)鏈從firstResponse開始接下來(lái)是它的父視圖,如果沒有父視圖直到它的控制器(如果有的話)再到window和application。

initial view可能是hit-test view或者是first responder,沒有處理事件。UIkit就會(huì)將該事件傳遞給next responder下一個(gè)響應(yīng)者,每個(gè)響應(yīng)者通過(guò)調(diào)用-nextResponder方法決定是處理該事件還是向響應(yīng)鏈的上層傳遞,直到某個(gè)響應(yīng)者處理了該事件或者沒有響應(yīng)者了為止。
需要注意的是,所有的響應(yīng)鏈都是父子視圖的關(guān)系,如果View A、View C、 VIew E只是視覺上遮蓋了,但是卻不是superview、subview的關(guān)系,則事件是不會(huì)在兩者之間傳遞的.
響應(yīng)者鏈順序如下:
Initial View -> View Controller(如果存在) -> superview -> · ·· -> rootView -> UIWindow -> UIApplication