iOS事件傳遞和響應機制

一部iOS設備會產生各類各樣的事件(UIEvent 實例)好比:觸摸屏幕、遠程控制等,這些事件發生了就須要有響應者(UIResponder 實例)去響應這些事件。這就須要一套事件響應機制。bash

事件類型

查看UIEventType的定義,咱們知道有4種事件類型。app

typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches,
    UIEventTypeMotion,
    UIEventTypeRemoteControl,
    UIEventTypePresses NS_ENUM_AVAILABLE_IOS(9_0),
};
複製代碼

其中UIEventTypeTouches就是觸摸手機屏幕產生的事件。UIEventTypeMotion咱們能接觸到的就多是手機的shake,也就是搖晃事件了。UIEventTypeRemoteControlUIEventTypePresses對於手機App開發者通常遇不到。UIEventTypePresses的名字有必定的迷惑性,其實它指的是物理按鍵被按下,好比電視的遙控器。ide

除了UIEventTypeTouches其餘事件咱們都難以遇到。ui

事件響應者和響應鏈

能響應事件的都是UIResponder及其子類。常見的UIResponder的子類有:UIViewUIViewControllerUIApplicationUIApplicationDelegatespa

下面這張圖是Apple官方文檔中的圖,能夠看出事件的響應鏈與視圖的層級關係基本一致。值得注意的是響應鏈的末端是...->UIWindow->UIApplication->UIApplicationDelegate。一個UIRespondernextResponder指向它的下一個響應者。你能夠重寫(override)nextResponder方法改變下一個響應者。實際上有些類已經重寫了nextResponder,好比一個UIView若是是UIController的根視圖,它的nextResponder會指向view controller,不然就會指向它的父視圖。詳見 Using Responders and the Responder Chain to Handle Events Altering the Responder Chain一節。code

事件響應鏈

若是順着響應鏈沒有發現可以響應事件的響應者,那麼這個事件就會被忽略cdn

咱們看到,UIResponder中對應每種事件類型都有對應的事件響應方法,好比對於touch事件來講有touchesBegan: withEvent:,touchesMoved: withEvent:,touchesCancelled: withEvent:,touchesEnded: withEvent:,還有對於motion事件來講有motionBegan: withEvent:,motionEnded: withEvent:,motionCancelled: withEvent:。還有針對其餘事件的方法,你能夠去UIResponder類裏去查看。blog

若是一個UIResponder子類重寫(override)了上述所說事件響應方法,那麼事件就算這個被這個類的實例響應了。事件就再也不會沿着響應鏈傳遞。 一個UIControl(UIButton是UIControl的子類),無論它有沒有被增長一個target,事件傳遞到它這裏就終止了。UIControl應該是實現了上面說的幾個響應事件的方法。事件

好比一個UIView子類重寫了touchesBegan: withEvent:,touchesMoved: withEvent:,touchesCancelled: withEvent:,touchesEnded: withEvent:,若是有觸摸事件傳遞到這個子類的實例,就會調用這些方法,並中止向下傳遞。圖片

事件傳遞機制

上面說的事件響應鏈有一個起點,這個起點叫作first responder。每一種事件都有找到或者指定first responder的規則,這裏咱們只關心觸摸事件(touch event)。

當手機屏幕被觸摸,系統將其包裝成觸摸事件(touch event)並傳遞給UIApplication,UIApplication傳遞給UIWindow(也是一個UIView的子類),UIWindow調用hitTest:withEvent:,獲得一個可以響應觸摸事件(touch event)的UIView

UIViewhitTest:withEvent:方法,會遍歷view的層級結構,找到包含特定touch的最深層的子視圖,稱爲觸摸事件的first responder。若是first responder不能響應事件,就會沿事件響應鏈傳遞這個事件直到被響應或者被忽略。

htiTest

上面的圖片展現了hitTest:withEvent:是如何實現的。其中幾個點咱們須要注意,首先判斷的是hidden,userInteractionEnable和alpha這幾個屬性,而後使用pointInside:withEvent:判斷點擊事件是否發生在該UIView內。而後是倒序遍歷全部子視圖並調用他們的hitTest:withEvent:方法。

事件傳遞機制原理的幾個應用

一、非矩形可點擊區域。好比一個button的一個圓形區域能夠被點擊,其餘區域不可點擊。只要重寫pointInside:withEvent:,在圓形區域的點返回YES,不在圓形區域的返回NO。

二、按鈕超出父視圖。默認狀況下,按鈕超出父視圖的部分是不能夠點擊的。咱們能夠重寫父視圖的pointInside:withEvent:方法,讓按鈕超出父視圖的部分也返回YES。

參考:

1. Using Responders and the Responder Chain to Handle Events

相關文章
相關標籤/搜索