iOS 響應者鏈

響應者鏈顧名思義就是由一系列能夠響應事件的響應者對象組成的一個層式結構。我們把具有響應和處理事件能力的對象稱為響應者對象。

  • 事件:有三種
    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上。

舉個??
例子.png

假設用戶觸摸了圖中的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。


響應鏈傳遞的順序.png

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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、響應者鏈(Responder Chain) 先來說說響應者對象(Responder Object),顧名思義,...
    像小強一樣活著閱讀 6,889評論 8 76
  • 1、響應鏈的傳遞 Responder一點也不神秘————iOS用戶響應者鏈完全剖析(建議全看)看完上面一篇應該能完...
    RasonWu閱讀 10,418評論 3 36
  • 好奇觸摸事件是如何從屏幕轉移到APP內的?困惑于Cell怎么突然不能點擊了?糾結于如何實現這個奇葩響應需求?亦或是...
    Lotheve閱讀 57,983評論 51 603
  • 當你設計一個app的時候,可能會有這樣的場景,你想動態的去響應一個事件。例如,在屏幕上的一個觸摸事件可能在不同的對...
    007Mango閱讀 20,081評論 12 40
  • 在iOS開發中經常會涉及到觸摸事件。本想自己總結一下,但是遇到了這篇文章,感覺總結的已經很到位,特此轉載。作者:L...
    WQ_UESTC閱讀 6,114評論 4 26