響應者鏈顧名思義就是由一系列能夠響應事件的響應者對象組成的一個層式結構。我們把具有響應和處理事件能力的對象稱為響應者對象。
事件:有三種
1、Touch Events,點擊事件;
2、Motion Events,移動事件,比如監聽加速器、陀螺儀 產生的事件;
3、Remote Control Events,遠程控制事件, 比如耳機,可以控制你的音量、播放音樂。響應對象
UIResponder 是所有具有響應對象的基類,我們熟悉的UIApplication、UIViewController、UIWindow 和所有繼承自UIView的 UIKit 都直接或間接的繼承自 UIResponder,所以它們的實例都是可以構成響應者鏈的響應對象。
當用戶點擊屏幕的時候,觸摸事件通過 hitTest:withEvent: 來確定first Response,該方法接收參數CGPoint 和 UIEvent,并從底層開始按照subView的順序測試該CGPoint在哪個View上,如果在該View上,則繼續測試是否在View的subview上。
舉個??
假設用戶觸摸了圖中的view E。iOS通過如下順序查找hit-test view
1.觸摸點在A里面,因此檢測子view B和C
2.觸摸點不在B里面,但是在C里面。因此檢測C的子View D和E。
3.觸摸點不在D里面,但是在E里面,并且E是在最外層的包含觸摸點的view,因此E就是要找的hit-test view
hitTest:withEvent:函數的實現代碼:
1.能否自己處理?不能,return nil;
2.點在不在當前控件上?沒在,return nil;
3.說明能處理觸摸事件,并且在當前控件上,是合適的控件,但不一定是最合適的。從后往前遍歷自己的子控件,是否是最合適的控件(包含該觸摸點的View)。如果是,返回該View。
4.說明沒找到比自己合適的View,返回自己。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event的實現:
[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
視圖包含這個點: pointInside:withEvent: ==true
hit-test view和響應鏈的概念:當一個事件發生需要處理時,會讓合適的對象去處理。如果是觸摸事件的話,該對象就是hit-test view。如果是其他事件,該對象指的就是第一響應者(響應鏈中)。響應鏈是一個比較大的范疇,在觸摸事件中,hit-test view就是響應鏈中的第一響應者。也就是說在觸摸事件中通過hitTest:withEvent:方法找到的hit-test view就是第一響應者。
下圖給出了沿著響應鏈傳遞的順序。兩個圖的區別是視圖的層次關系不一樣。響應鏈從firstResponse開始接下來是它的父視圖,如果沒有父視圖直到它的控制器(如果有的話)再到window和application。
initial view可能是hit-test view或者是first responder,沒有處理事件。UIkit就會將該事件傳遞給next responder下一個響應者,每個響應者通過調用-nextResponder方法決定是處理該事件還是向響應鏈的上層傳遞,直到某個響應者處理了該事件或者沒有響應者了為止。
需要注意的是,所有的響應鏈都是父子視圖的關系,如果View A、View C、 VIew E只是視覺上遮蓋了,但是卻不是superview、subview的關系,則事件是不會在兩者之間傳遞的.
響應者鏈順序如下:
Initial View -> View Controller(如果存在) -> superview -> · ·· -> rootView -> UIWindow -> UIApplication