iOS事件傳遞與響應(一)——傳遞

在實際的開發中會經常遇到處理事件的操作,對於我們這樣的新手而言,會遇到的一是事件發生後在什麼時候去處理這個事件,二是在事件處理的時候會得不到響應等問題;鑑於此,明確關於事件是如何發生,如何傳遞以及最後是誰來做響應處理的是非常有必要的。以觸摸事件爲例,來具體闡述:

本文首先從事件的傳遞來說起,也就是來解決以下問題:

  • 事件是如何產生的?
  • 點擊屏幕的觸摸事件是如何從屏幕轉移到應用內的?
  • 系統是如何將點擊事件傳遞到window上;

一、相關術語:觸摸,事件以及響應者

1、觸摸:UITouch

    手指觸摸屏幕產生對應的UItouch對象,以下觸摸的各個階段的狀態;觸摸發生在屏幕上,是人爲操作產生;

作爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流羣:834688868,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經驗,討論技術, 大家一起交流學習成長!

2、事件:UIEvent

    觸摸的目的是生成觸摸事件供響應者響應,一個觸摸事件對應一個UIEvent對象,其中的type屬性標識了事件的類型(事件可以分爲很多種,不止是觸摸事件)。UIEvent對象中包含了觸發該事件的觸摸對象的集合,因爲一個觸摸事件可能是由多個手指同時觸摸產生的。觸摸對象集合通過allTouches屬性獲取。

以下是UIEventType,標識了 不同的事件,包括加速器事件,遠程控制事件,移動事件,滾動事件等等。

3、響應者:UIResponder

在ios中不是所有對象都能接收到事件,繼承於UIResponder的對象纔可以接收並處理事件,這些被稱爲“響應者對象”;

    UIView,UIViewController,UIApplication,Appdelegate響應者能夠響應事件,是因爲UIResponder中提供了四個事件處理的方法:

通過以上的闡述,很清楚的能夠明白,觸摸產生觸摸事件,響應者來對觸摸事件進行響應。這些瞭解幫助我們解決第一個問題,事件是如何產生的,同樣以觸摸事件爲例,人爲的觸摸屏幕,系統生成對應的UITouch對象, 有了UITouch對象後同時會對應生成UIEvent對象,一個UITouch對象對應一個UIEvent對象,此時,會產生我們所說對應的觸摸事件。而後,該事件進行傳遞找到最佳的響應者對象對其進行響應處理。

到此仍然沒有解決我們事件是如何傳遞的問題,那繼續。

二、系統如何將點擊事件傳遞到window上;

當用戶觸摸屏幕時,就會產生對應的觸摸事件,經過IPC進程間通信,事件最終被傳遞到了合適的應用內;觸摸事件從觸屏產生後,由IOKit將觸摸事件傳遞給SpringBoard進程,再由SpringBoard分發給當前前臺APP處理。以下是處理的簡單流程圖:

     通過以上的流程之後,我們的觸摸就會被轉入對應app或者系統桌面上來做處理;這裏會引入我們的runloop的內容,runloop事件循環機制等,後續再展開;所有的事件最終會被加入到runloop循環當中,等待處理;

    總結:當一個外界的事件(觸摸,搖晃,重力事件等)發生後,首先由IOKit.framework生成一個IOHIDEvent事件並且會交由SpringBord來接收。SpringBord接收該事件後,通過mac port轉發給當前對應的app進程,隨後蘋果註冊的那個source1 就會觸發回調,並調用_UIApplicationHandleEventQueue()進行應用內部的分發。

相關的術語概念:

  • IOKit是是一個系統框架的集合,用來驅動一些系統事件;用來處理接收和處理硬件事件,獲取設備的相關信息等作用的庫;在這裏用來接收觸摸事件並且生成對應的IOHIDEvent對象;

  • IOHIDEvent中的 HID 代表 Human Interface Device,即人機交互驅動

  • mach port 進程端口,各進程之間通過它進行通信。

  • SpringBoad.app 是一個系統進程,可以理解爲桌面系統,可以統一管理和分發系統接收到的觸摸事件。(不止是觸摸事件,SpringBoad接收按鍵,觸摸,加速,重力傳感等事件);

    自此,對於一個事件是如何產生並且是如何從用戶行爲轉移到手機屏幕的window上的,有了一定的答案;那麼,事件最終會被分配到桌面系統或者是對應的app進程中來做處理,那麼,如何來找到最佳的事件響應者,在app內部的傳遞過程又是如何的?接下來:
    

三、事件在App內部的傳遞

   在通過從用戶觸摸屏幕,到通過IPC進程間通信,由IOKit將觸摸事件傳遞給SpringBoard進程,再由SpringBoard分發給當前前臺App處理。此時,該事件到達App,App進程的mach port接受到SpringBoard進程傳遞來的觸摸事件,主線程的runloop被喚醒,觸發了source1回調。source1回調又觸發了一個source0回調,將接收到的IOHIDEvent對象封裝成UIEvent對象,此時App將正式開始對於觸摸事件的響應。

如下圖所示,是事件在App內部的傳遞過程:

    如上圖所示是模擬的app內部的視圖,視圖層級爲A( B(D,E) , C(G,F));A中加入B和C,B中有D和E,C中有F和G;

那麼,第一:當觸摸事件發生在F所在的區域時,事件的傳遞和響應過程如下所示:

判斷是否能夠響應該事件,從四個方面:(允許交互,不隱藏,透明度以及子視圖沒有超過父視圖的有效範圍也就是點擊區域);

  • UIApplication先將事件傳給A判斷自身是否能夠響應該事件;
  • 如果能,那A繼續將該事件傳給C(從後向前進行詢問查詢);
  • C判斷自己是否能夠響應該事件,如果能,C繼續將該事件傳給G;
  • G判斷自己不能夠響應快該事件,那麼,回退到C,C繼續將該事件傳給F;
  • F判斷自己可以響應該事件,並且F已經沒有子視圖,所以F本身就是事件的最終響應者;

第二:當觸摸事件發生D在所在的區域且不在其父視圖****B****的範圍內時,事件的傳遞和響應過程如下所示:

  • UIApplication先將事件傳給A判斷自身是否能夠響應該事件;
  • 如果能,那A繼續將該事件傳給(從後向前進行詢問查詢);
  • C判斷自己是否能夠響應該事件;
  • C不能響應該事件,回退到A,A繼續將該事件傳給B;
  • B判斷自己可以響應該事件,B能夠響應;
  • B將該事件傳給E;
  • E判斷自己不能響應該事件,回退到B;
  • B又將該事件穿給D,D判斷自己也不能響應該事件(事件點擊區域不在D的父視圖範圍內);
  • 回退到A,A已經沒有子視圖可以傳遞事件,那麼A就是最終的事件響應者;

以上是兩種事件傳遞的情況,一是正常情況,二是異常情況;總的來說事件在App中的傳遞過程總結如下流程圖所示:

也就是說:事件在App內部的傳遞流程是從後向前不斷查找,找到最終最佳響應者;以上的過程就事件從產生到找到最佳響應者的過程;

事件的傳遞遵循的原則:同級從後向前傳遞;到此 ,本文對於事件的產生和傳遞做了一些論述(參考相關內容),那麼接下來通過簡單的示例來加以驗證。

那麼,上面提到,判斷當前視圖是否可以響應事件從四個方面來看:

  • 是否允許交互:將響應者對象的userInteractionEnabled屬性設爲NO,表示不允許交互;爲YES時表示允許交互;

  • 是否隱藏:hidden = YES;如果父視圖隱藏,那麼子視圖也會隱藏,隱藏的視圖無法接收事件;hidden = NO,不隱藏;

  • 透明度的量級:alpha < 0.01如果設置一個視圖的透明度<0.01,會直接影響子視圖的透明度。alpha:0.0~0.01爲透明。

  • 子視圖是否超出了父視圖的有效範圍;

    在事件的傳遞過程中,每經過一個視圖時都需要做以上的判斷,那麼,app內部是如何來做這些判斷的呢?由此,就必須提到一個方法:hitTest方法;
    

四、hitTest方法

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;

   hitTest是UIView中的一個方法,每一個響應者對象都會有一個對應的hitTest方法,用來判斷是否可以響應事件並傳遞事件;

    以下是往上給出的hitTest的判斷邏輯:

所以hitTest的作用有兩個:

  1. 一是用來詢問事件在當前視圖中的響應者,返回的是最終響應這個的事件的響應者對象;
  2. 二是事件傳遞的一個橋樑;

本節總結:

    本節從事件的發生,事件如何從屏幕傳到window上,以及事件在app內部的傳遞流程等來介紹事件的傳遞相關內容;回答文章開頭的問題。同時在事件傳遞的過程中很重要的一個方法hitTest方法,對其做簡單的說明。

  回顧問題:
  • 一、事件是如何產生的?
  • 二、事件是如何從屏幕中到達window上?
  • 三、事件在App內部的傳遞過程?

補充問題:當在代碼實現過程中,發現事件沒法響應,應該從哪幾個方面去查找?

初學者的簡單自學。

作爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流羣:413038000,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經驗,討論技術, 大家一起交流學習成長!

作者:一條道走到黑
鏈接:https://juejin.cn/post/6901202474880172040

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章