ios中的響應(yīng)者鏈-Responder Chain

響應(yīng)者鏈工作原理

應(yīng)用程序使用Responder對象接收和處理時間,響應(yīng)者對象是UIResponder類的任何實例,常見的子類包括UIViewUIViewControllerUIApplication.響應(yīng)者收到原始事件數(shù)據(jù),并且必須處理該事件或?qū)⑵滢D(zhuǎn)發(fā)給另一個響應(yīng)者對象,當(dāng)您的應(yīng)程序收到一個事件時,UIKit會自動將該事件指向最合適的響應(yīng)者對象,稱為第一響應(yīng)者,未處理的事件從響應(yīng)者傳遞到活動響應(yīng)器中的響應(yīng)者,這是應(yīng)用程序的響應(yīng)者對象的動態(tài)配置,應(yīng)用程序中沒有單個響應(yīng)者鏈,UIKit定義了將對象從一個響應(yīng)者傳遞到下一個響應(yīng)器的默認(rèn)規(guī)則,但是您可以隨時通過覆蓋響應(yīng)者對象中相對應(yīng)的屬性來更改這些規(guī)則.下圖顯示了應(yīng)用程序的默認(rèn)響應(yīng)者鏈,其界面包含Label,textField,button和兩個背景視圖,如果label不處理事件,UIKit會將事件發(fā)送到其superView對象,然后是窗口的根視圖,從根視圖響應(yīng)者鏈轉(zhuǎn)移到擁有該視圖的控制器,然后將事件傳遞給window,如果window不處理,UIKit將事件傳遞給UIApplication對象,并且可能將該事件傳遞給AppDelegate,

1.png

對于每種類型的事件,UIKit指定一個第一響應(yīng)者,并首先發(fā)送事件到這個對象,第一響應(yīng)者根據(jù)事件的類型而有所不同.

  • Touch events
    第一響應(yīng)者是發(fā)生觸摸的視圖
  • Press events
    第一響應(yīng)者是有焦點的響應(yīng)者
  • Shake-motion events
    第一響應(yīng)者是(UIKit)指定為第一個響應(yīng)者的對象
  • Remote-control events
    第一響應(yīng)者是(UIKit)指定為第一個響應(yīng)者的對象
  • Editing menu messages
    第一響應(yīng)者是(UIKit)指定為第一個響應(yīng)者的對象

與加速器,陀螺儀和磁力計相關(guān)的運動事件不遵循響應(yīng)者連,Core Motion將這些事件直接傳遞給你指定的對象,(https://developer.apple.com/library/content/documentation/Miscellaneous/Conceptual/iPhoneOSTechOverview/CoreServicesLayer/CoreServicesLayer.html#//apple_ref/doc/uid/TP40007898-CH10-SW27)

當(dāng)手指觸摸到屏幕中,觸摸到某一個控件到響應(yīng)這個事件分為兩步:事件的傳遞與分發(fā),和事件的響應(yīng)
事件的傳遞涉及到UIView中的兩個方法

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
//判斷當(dāng)前點擊事件是否存在最優(yōu)響應(yīng)者(First Responder)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
//判斷當(dāng)前點擊是否在控件的Bounds之內(nèi) 

UIKit使用基于視圖的命中測試來確定觸摸事件發(fā)生的位置。 具體來說,UIKit將觸摸位置與視圖層次結(jié)構(gòu)中的視圖對象的邊界進(jìn)行比較。 UIView的hitTest(_:with :)方法行進(jìn)視圖層次結(jié)構(gòu),尋找包含指定觸摸的最深的子視圖。 該視圖成為觸摸事件的第一響應(yīng)者

注意如果觸摸位置在視圖的邊界之外,則hitTest(_:with :)方法會忽略該視圖及其所有子視圖。 因此,當(dāng)視圖的clipsToBounds屬性為false時,即使它們包含觸摸,也不會返回該視圖邊界之外的子視圖
事件的傳遞其實就是在事件產(chǎn)生于分發(fā)之后如何尋找最優(yōu)響應(yīng)的一個過程

你可以修改響應(yīng)者對象的next屬性來更改響應(yīng)者鏈,當(dāng)你修改了此屬性時下一個響應(yīng)者就是你返回的對象,
許多UIKit類已經(jīng)覆蓋此屬性并返回特定對象。

  • UIView對象,如果這個控制器的View是根視圖,那么next responder 就是viewController,否則,就是這個view的父視圖

  • UIViewController對象,

    • 如果該controller的view是window的根視圖,那么next responder就是window
    • 如果controller是由另一個controller presented的,那么next responder是the presenting Controller.
  • UIWindow對象,next responder 就是UIApplication對象

  • UIApplication對象,那么 next responder 就是 app delegate ,但前提是appDelegateUIResponder的一個實例,而不是view,viewcontroller或者是app本身.


                                 (以上來自官方文檔)

事件的傳遞過程

1.觸碰屏幕產(chǎn)生事件UIEvent并存入UIApplication中事件隊列中,并在整個視圖結(jié)構(gòu)中自下而上進(jìn)行分發(fā),如下圖
2.UIWindow 接收到事件開始進(jìn)行最優(yōu)響應(yīng)視圖查詢過程(逆序遍歷子視圖)
3.當(dāng)?shù)?code>UiviewController這一層時同樣對其視圖開始最優(yōu)響應(yīng)視圖查詢,該查詢會調(diào)用上述提到的兩個方法,采用逆序查詢也是為了優(yōu)化查找速度,畢竟后添加的視圖易于命中

3831145-33ef2920581a014d.png

命中查找流程

1.調(diào)用hitTest方法進(jìn)行最優(yōu)響應(yīng)視圖查找

  • hidden = YES;
  • userInteractionEnabled = NO;
  • alpha < 0.01
    這三種情況hitTest方法會返回nil ,即當(dāng)前視圖下無最有相應(yīng)視圖,無法響應(yīng)事件

2.hitTest方法內(nèi)部調(diào)用pointInside方法對點擊進(jìn)行是否在當(dāng)前視圖bounds內(nèi)進(jìn)行判斷,如果超出bounds范圍,則hitTest返回nil,未超出范圍繼續(xù)步驟3
3.對當(dāng)前視圖下的subviews進(jìn)行逆序步驟1,2查詢最優(yōu)響應(yīng)者,如果hitTest返回視圖,則說明當(dāng)前視圖有最優(yōu)響應(yīng)者,可能是self也可能是subview,

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    
    if (self.alpha < 0.01 || !self.userInteractionEnabled || self.hidden) {
        
        return nil;
    }
    
    if (![self pointInside:point withEvent:event]) {
        
        return nil;
    }
    
    __block UIView *hitView = nil;
    [self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
        
        hitView = [subview hitTest:point withEvent:event];
        if (hitView) {
            
            *stop = YES;
        }
    }];
    
    return hitView ? : self;
}

總結(jié)

  • 事件分發(fā)與傳遞:自上而下(UIApplication-window-.....)
  • 事件響應(yīng):自下而上(view-superView-.........)
3831145-8f25188c47eacafd.jpg

打印結(jié)果:


點擊CView

參考:http://www.itdecent.cn/p/eacb86714799.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 用戶以多種方式操縱他們的iOS設(shè)備,例如觸摸屏幕或搖動設(shè)備。 iOS會解釋用戶何時以及如何操作硬件并將此信息傳遞到...
    坤坤同學(xué)閱讀 4,122評論 7 19
  • 在iOS開發(fā)中經(jīng)常會涉及到觸摸事件。本想自己總結(jié)一下,但是遇到了這篇文章,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 6,249評論 4 26
  • iOS開發(fā)中的事件處理 理論非原創(chuàng),是對網(wǎng)上資料的整理以及Demo驗證 一. UIResponder 1.1 事件...
    喪心病狂樂閱讀 805評論 0 0
  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點擊了?糾結(jié)于如何實現(xiàn)這個奇葩響應(yīng)需求?亦或是...
    Lotheve閱讀 59,543評論 51 604
  • 喬幫主在發(fā)布會上提到,用戶的手才是最好的輸入設(shè)備,的確,iPhone之后,非觸屏手機再已難覓。觸摸是最基本的用戶輸...
    皮皮Warrior閱讀 2,376評論 3 48

友情鏈接更多精彩內(nèi)容