iOS事件傳遞:響應者鏈[譯]

事件傳遞:響應者鏈

當你設計一個app的時候,你很可能需要你的app能夠動態(tài)響應某些事件。比如,觸摸可以發(fā)生在屏幕上不同對象上,你需要決定哪些對象來響應一個特定的事件,并了解對象是如何接收事件。

當一個用戶事件產生的時候,UIKit創(chuàng)建一個事件對象,這個對象包含了如何處理這個事件的信息。然后將這個事件對象放到這個活躍app的事件隊列中。對于觸摸事件,這些對象都到包裹成了UIEvent對象。對于運動事件,取決于你使用的框架和你感興趣的運動事件的類型。

一個事件沿著一個特定的路徑,知道它被傳遞到一個能處理這個事件的對象。首先,事件最先被UIApplication單例對象獲取,這個對象還負責這個對象的分發(fā)。典型的,這事件被傳遞到app的主窗口對象,也即使把時間傳遞給一個初始對象來處理。初始對象取決于事件的類型。

  • 觸摸事件
    對于觸摸事件,主窗口對象首先嘗試把事件傳遞給觸摸發(fā)生的視圖對象上。這個對象是一個hit-test視圖對象。找到hit-test視圖對象的過程叫hit-testing。下面會詳細說明:
  • 運動事件和遠程事件
    對于這兩類事件,主窗口對象把事件發(fā)送給第一響應者。

最終的目標是找到一個對象能處理這個事件和響應這個事件。因此,UIkit對象首先把事件發(fā)送給最適合處理這個事件的對象。對于觸摸事件,這個對象是hit-test視圖,對于其他的事件,這個對象是第一響應者。接下來的部分會詳細說明hit-test視圖和 第一響應者是如何被決定的。

Hit-Testing 返回觸摸發(fā)生的視圖

iOS使用hit-testing來尋找被觸摸的視圖。Hit-testing包括檢查一個觸摸是否發(fā)生在相關的視圖對象上。如果是,就遞歸的去檢查這個視圖的所有子視圖。最底層視圖包含了觸摸點的視圖成為hit-test視圖。在知道hit-test視圖之后,事件交給這個視圖來處理。

`Hit-testing`返回被觸摸的子視圖
`Hit-testing`返回被觸摸的子視圖

說明:假設用戶觸摸了視圖E在上圖中。iOS通過以下檢查子視圖的順序來找到hit-test視圖:

  • 1.觸摸事件發(fā)生在視圖A的邊界內。所以檢測它的子視圖B和C。
  • 2.觸摸沒有發(fā)生在B,但是發(fā)生在C內。所以檢測C的子視圖D和E。
  • 3.觸摸沒有發(fā)生在D,但是發(fā)生在E內。

視圖E是包含觸摸點堆棧視圖上最底層的視圖。所以視圖E成為hit-test視圖。
方法 hitTest:withEvent:根據(jù)一個給定的CGPointUIEvent返回指定的hit-test視圖。hitTest:withEvent:方法首先調用pointInside:withEvent:開始,如果傳遞到hitTest:withEvent:的點在這個視圖的bounds內,那么pointInside:withEvent:返回YES。繼而,在返回YES的所有子view上遞歸調用hitTest:withEvent:。

如果傳遞hitTest:withEvent:的點不在視圖的邊界內,第一次調用pointInside:withEvent:方法會返回NO,這個點會被忽略,hitTest:withEvent:方法返回nil.如果一個子視圖返回NO。那么這個子視圖的所有堆棧上的視圖全部被忽略。因為觸摸沒有發(fā)生在這個子視圖上,就更不可能發(fā)生在其子視圖的子視圖上了。這意味著如果一個子視圖超出了其父視圖的邊界,則超出邊界的部分是不會響應事件。這部分父視圖都無法接收時間,更不用說往下傳遞了。如果子視圖的clipsToBounds屬性被設置為NO。

注意:一個觸摸事件在其生命周期內,跟這個hit-test視圖綁定,即便這個觸摸事件滑出了當前視圖。

這個hit-test視圖被給予第一優(yōu)先權處理這個觸摸事件。如果這個視圖不能處理這個事件,這個觸摸事件順著響應者鏈向上傳遞,直到找到第一個能處理這個事件的對象。

響應者鏈是由一系列響應者連成的鏈

很多類型的事件的傳遞都依賴于響應者鏈。響應者鏈是一系列連接在一起的響應者對象。它從第一響應者開始,以application對象結束。如果第一響應者不能處理這個事件,它會把這個事件沿著這個響應者鏈傳遞到下一個響應者。

一個響應者對象是一個能響應并能處理事件的對象。UIResponder類是所有響應者的父類,它定義了事件處理和常見響應者行為的通用編程接口。UIApplicationUIViewController以及UIView類的實例對象都是響應者,這表明,所有視圖和絕大多數(shù)主控制器都是響應者。需要注意的是核心動畫的圖層對象不是響應者。

第一響應者被指定為首先接收事件的對象。通常,第一響應者是一個視圖對象。一個對象要成為第一響應者,需要滿足下面兩個條件:

  • 1.重寫canBecomeFirstResponder方法并返回YES
  • 2.接收becomeFirstResponder消息。如果有必要,一個對象可以給自己發(fā)送這個消息。

注意:在指定一個對象為第一響應者之前,要確保這個對象在其合適的生命周期內。比如,通常在重寫的viewDidAppear:方法中調用becomeFirstResponder方法。如果你試圖在viewWillAppear:給一個第一響應者賦值,由于對象還沒有創(chuàng)建完成。那么becomeFirstResponder方法會返回NO。

事件并不是唯一依賴于響應者鏈的對象。響應者鏈在以下處理中都會用到:

  • 觸摸事件 如果hit-test視圖不能處理這個觸摸事件,則從這個視圖開始順著響應者鏈向上級傳遞。
  • 運動事件 UIKit框架的對象如果要處理搖晃事件,第一響應者必須要實現(xiàn)UIResponder類的motionBegin:withEvent:motionEnded:withEvent:方法。
  • 遠程事件 如果要處理遠程事件,第一響應者比如要實現(xiàn)UIResponder類的remoteControlReceivedWithEvent:方法。
  • Action消息 當用戶在操縱一個控件時,例如一個按鈕或者一個開關,這個action方法的target為空,這個消息會順著響應者鏈從第一響應者(可能是這個控件自身)往后傳遞。
  • 快捷菜單消息 當用戶點擊了快捷菜單的菜單時,iOS使用響應者鏈來找到一個實現(xiàn)了必要方法的對象來處理。(實現(xiàn)cut:, copy:, paste:)
  • 文本編輯 當用戶點擊一個textField或者textView,則這個文本控件自動變成第一響應者。默認的響應是彈出虛擬鍵盤,這個文本控件獲得焦點,可以開始編輯。你還可以根據(jù)應用的需要自定義鍵盤。你還可以給任何響應者對象添加自定義的輸入控件。

UIKit自動將用戶點擊的文本控件設置為第一響應者,其他對象,則需要顯示的調用becomeFirstResponder方法來成為第一響應者。

響應者鏈的路徑

如果初始對象,hit-test視圖或者第一響應者不能處理事件。UIKit把事件傳遞給響應者鏈上的下一個響應者。每一個響應者決定是否處理這個事件或者是把這個事件繼續(xù)傳遞給下一個響應者,使用nextResponder方法。直到找到一個響應者或再也沒有別的響應者。一個響應者序列,從iOS檢測到事件并傳遞給初始對象開始,通常是一個視圖。這個初始視圖有第一優(yōu)先權來處理這個事件。下圖顯示了兩種事件傳遞路徑對于不同的應用配置。一個應用的事件傳遞路徑由其具體的結構決定,但遵循著同樣的規(guī)律。

iOS響應者鏈
iOS響應者鏈

對于左圖,事件傳遞路線

  • 1.initial view試圖處理這個事件或消息。如果它不能處理這個事件,它把事件傳遞給它的父視圖,因為initial view不是控制器的根視圖。
  • 2.父視圖試圖處理這個事件。如果它也不能處理這個事件,則繼續(xù)傳遞給它的父視圖,因為當前視圖還不是控制器的根視圖。
  • 3.控制器的根視圖試圖處理這個事件,如果根視圖還不能處理這個事件,則把事件傳遞給它的控制器。
  • 4.控制器視圖處理這個事件,如果不能,則把事件傳遞給窗口對象。
  • 5.如果窗口對象還不能處理它,則把事件傳遞給單例的application對象。
  • 6.如果單例的application對象也不能處理,則事件被忽略。

右邊的情形有點稍微的不同,但都遵循著以下規(guī)律:

  • 1.事件向上傳遞,直到遇到某個控制器的根視圖。
  • 2.根視圖把事件傳遞給它的控制器。
  • 3.控制器接著把事件傳遞給包含它的視圖,一直傳遞到另一層根視圖。1~3步重復,直到找到根控制器。
  • 4.根控制器把事件傳遞給窗口對象。
  • 5.窗口對象把事件傳遞給application對象。

重要:如果你實現(xiàn)一個自定義視圖來處理遠程事件,action消息,UIKit運動事件,快捷菜單消息。不要直接把事件或者消息傳遞給下一個響應者。取而代之的是,你應該先調用父類的實現(xiàn),讓UIKit為你處理響應者鏈的遍歷。

譯自:Event Delivery: The Responder Chain

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容