iOS事件分發(fā)機制介紹與應用

iOS事件分發(fā)機制介紹與應用

打開某App點擊登錄按鈕后彈出登錄頁面。這是一個事件分發(fā)與響應的示例。我們來探究下該過程。

介紹事件分發(fā)機制自然繞不開事件。

iOS包含以下幾種類型事件:

  • 觸摸事件
  • 按壓時間
  • 搖動事件
  • 遠程控制事件
  • 編輯菜單時間

本文選擇以觸摸事件進行介紹。因為相對而言它包含的事件分發(fā)機制最全面。

iOS事件分發(fā)流程

iOS應用啟動后UIApplicationMain函數(shù)會創(chuàng)建一個UIApplication的單例。該單例會維護一個FIFO的隊列進行事件分發(fā)。系統(tǒng)檢測到觸摸事件后就會發(fā)給當前Application單例,由該單例進行派發(fā)。派發(fā)分為下面三個過程:

一、hitTest

UIWindow一旦接收到事件后就進行hit-test以查找哪個對象接收該事件。hitTest:withEvent方法用于發(fā)現(xiàn)在觸摸位置的視圖。pointInside:withEvent:用于檢測該點擊是否在視圖的邊界內(nèi)。hitTest:withEvent調(diào)用pointInside:withEvent:。

hitTest以遞歸的形式調(diào)用直至找到能處理觸摸事件的最頂部葉子視圖(一般就是手指點中區(qū)域所屬的視圖),那么該視圖就會被選中。稱為第一響應者。

做個類比就是:老板說我這有個事誰干給干了?經(jīng)過經(jīng)理等層層指派,大家都覺得小王合適。

該過程可以通過復寫下面方法斷點了解。

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

具體情況如圖所示:

hit_test.png

二、sendEvent

一旦確定第一響應者后,UIApplication單例就會發(fā)送相關(guān)觸摸事件到第一響應者。

就好比老本拍板說那就讓小王干了。

該過程可以斷點button的處理方法進行了解。如圖所示:


sent_enent

三、事件處理

針對觸摸事件的處理是重載如下方法:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

第一響應者接到事件后有以下三種選擇:

  1. 什么都不不處理(選擇不重載上述方法)
  2. 處理一部分,剩下的交由其它對象處理(重載處理后調(diào)用super)
  3. 獨自進行處理(重載處理并且不調(diào)用super)

如果第一響應者什么都不處理或者處理部分然后調(diào)用super,則事件將被發(fā)送到一個鏈式響應路徑中,即響應鏈。該事件遵循以下路徑進行轉(zhuǎn)發(fā):

  1. 第一響應者
  2. 第一響應者的父視圖
  3. 父視圖的父視圖,直至關(guān)聯(lián)的視圖控制器
  4. 視圖控制器的父視圖控制器,直至根視圖
  5. 根視圖下一響應者是window
  6. window的下一響應者是application
  7. 最后的響應者是App delegate

若轉(zhuǎn)發(fā)至App delegate,且App delegate未進行處理則該事件將被丟棄。

iOS處理方式

那么事件處理的方式有哪些?我們大致可以劃分為下面三類。

  1. UIControl的子類
  2. TouchEvent
  3. 觸摸相關(guān)手勢。

有可能一個對象同時實現(xiàn)了兩個或者更多處理方式。那么誰的優(yōu)先級高些呢?

這個我們可以做個類比,讓你實現(xiàn)判斷鼠標單機還是雙擊,那么檢測單擊的時間會比雙擊要長。因為雙擊失效后才會進行單擊判定。
同理手勢識別不了了,其它才有機會處理,UIControl子類也類似,那么優(yōu)先級如下所示:

觸摸相關(guān)手勢 > UIControl的子類 > TouchEvent

一般情況下,我們遇到的是手勢觸發(fā)了就不會繼續(xù)轉(zhuǎn)發(fā)事件了。
這就是為什么有人在UIButton中添加個tap手勢看起來正常(別笑)。

事件分發(fā)機制應用

事件分發(fā)不同場景方法選擇

  1. 更改事件分發(fā)流程,實現(xiàn)相關(guān)類的hitTest:withEvent:方法為佳。

  2. 擴大點擊范圍,覆寫pointInside:withEvent:是個不錯的選擇。

  3. 解決一寫如收起鍵盤等問題,可嘗試直接發(fā)送事件。如:

    [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
    
  4. 選擇讓父視圖或者關(guān)聯(lián)視圖控制器處理事件可以這么寫(雖然很少見有這么玩的):

    [button addTarget:nil action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
    

具體應用場景

實現(xiàn)自定義懸浮窗

根據(jù)響應鏈規(guī)則,我們能更好的設(shè)計一個自定義懸浮窗。

  1. 懸浮窗作為UIWindow的rootController存在
  2. 覆寫pointInside:withEvent:判斷事件所屬UIWindow
  3. 完善自己的事件命中測試,實現(xiàn)視圖層級管理

詳情參加Flex寫法。

模擬點擊

通過了解分析事件分發(fā)過程,我們能很清楚的知道要實現(xiàn)模擬點擊我們只需把點擊事件發(fā)送給UIApplication單例的隊列就可以了。

  1. 創(chuàng)建UIEvent對象。
  2. 發(fā)送UIEvent對象[[UIApplication sharedApplication] sendEvent:event]。

統(tǒng)計與修改UITouch

  1. hook UIApplication類的- (void)sendEvent:(UIEvent *)event;方法
  2. 進行統(tǒng)計或修改

注:創(chuàng)建UITouch對象和對UITouch對象進行修改,可以參考kif-framework。

參考資料

  1. flex
  2. kif-framework
  3. using responders and the responder chain to handle events
最后編輯于
?著作權(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)容

  • 重點參考鏈接: View Programming Guide for iOS https://developer....
    Kevin_Junbaozi閱讀 4,700評論 0 15
  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點擊了?糾結(jié)于如何實現(xiàn)這個奇葩響應需求?亦或是...
    Lotheve閱讀 59,488評論 51 604
  • 在iOS開發(fā)中經(jīng)常會涉及到觸摸事件。本想自己總結(jié)一下,但是遇到了這篇文章,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 6,246評論 4 26
  • 本質(zhì)上是鄉(xiāng)下人,他的根,他的魂不在城里,笨拙、遲鈍的標簽盡顯。本是簡單純真的人,哪能適應城里人復雜狡詐的人際關(guān)系?...
    小小佘閱讀 350評論 0 1
  • 一、概念 二、代碼
    liyuhong閱讀 238評論 0 0

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