事件響應鏈

一、相關類介紹

1、UIResponder

? ? ? iOS中只有繼承了 UIResponder 的對象才能接收并處理事件。UIResponder 的派生體系如下:

下面是 UIResponder 的接口:

?@interface UIResponder : NSObject

// 響應者相關的屬性和方法

@property(nonatomic, readonly, nullable) UIResponder *nextResponder;

@property(nonatomic, readonly) BOOL canBecomeFirstResponder;?

- (BOOL)becomeFirstResponder; @property(nonatomic, readonly) BOOL canResignFirstResponder;?

- (BOOL)resignFirstResponder;?

@property(nonatomic, readonly) BOOL isFirstResponder;?

?// 觸摸事件?

- (void)touchesBegan:(NSSet*)touches withEvent:(nullable UIEvent *)event;

- (void)touchesMoved:(NSSet*)touches withEvent:(nullable UIEvent *)event;

- (void)touchesEnded:(NSSet*)touches withEvent:(nullable UIEvent *)event;?

- (void)touchesCancelled:(NSSet*)touches withEvent:(nullable UIEvent *)event;?

- (void)touchesEstimatedPropertiesUpdated:(NSSet*)touches NS_AVAILABLE_IOS(9_1);

// 關于3Dtouch的按壓事件?

- (void)pressesBegan:(NSSet*)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);?

- (void)pressesChanged:(NSSet*)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);?

- (void)pressesEnded:(NSSet*)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);?

- (void)pressesCancelled:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

// 加速計事件

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

// 遠程事件

- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);

- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0);

// 其它

- (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0);

@property(nullable, nonatomic,readonly) NSUndoManager *undoManager NS_AVAILABLE_IOS(3_0);

@end

1.1瘤运、UIResponder 遵守的協(xié)議 UIResponderStandardEditActions,負責處理 復制犁嗅、粘貼 等操作。?

1.2晤碘、UIResponder 可以處理4種事件,分別是:觸摸事件园爷、按壓事件(3Dtouch)宠蚂、加速計事件(搖一搖)童社、遠程事件(耳機操控事件)求厕,當事件發(fā)生時,UIResponder 就會調用對應的方法扰楼。?

1.3呀癣、處理事件時涉及2個對象:UITouch、UIEvent灭抑。(UIPress十艾、UIPressesEvent不作介紹) ?

1.3.1、UITouch?

UITouch是處理手指觸屏交互的底層對象腾节,保存著對應的時間、階段等相關信息荤牍。當有一根手指觸摸屏幕時就會產生一個UITouch對象案腺,當該手指移動時,該UITouch對象的屬性就會不斷更新康吵,當手指離開屏幕時劈榨,該UITouch對象就會消亡,所以其生命周期是從手指接觸屏幕開始晦嵌,到手指離開屏幕結束同辣。下面是相關屬性和方法:?

// 觸摸點所在的窗口?

@property(nonatomic,readonly,retain) UIWindow *window;?

// 觸摸點所在的視圖?

@property(nonatomic,readonly,retain) UIView *view;?

// 短時間內點按屏幕的次數,可以根據tapCount判斷單擊惭载、雙擊或多點擊?

@property(nonatomic,readonly) NSUInteger tapCount;?

// 觸摸事件產生或變化時的時間旱函,單位是s?

@property(nonatomic,readonly) NSTimeInterval timestamp;?

// 當前觸摸事件的狀態(tài)?

@property(nonatomic,readonly) UITouchPhase phase;?

// 觸摸點在view上的位置(左上角為原點),參數view為nil時描滔,返回觸摸點在UIWindow上的位置?

- (CGPoint)locationInView:(UIView *)view;?

// 前一個觸摸點的位置?

- (CGPoint)previousLocationInView:(UIView *)view;?

1.3.2棒妨、UIEvent?

UIEvent表示事件對象,記錄事件產生的時刻和類型含长,可以將短時間內發(fā)生的觸摸屏幕的多個UITouch視為一個事件券腔,看作一個特定的手勢或動作伏穆。UIEvent對象的生命周期也是從手指觸摸屏幕開始到手指離開屏幕結束(如果有多個手指,以這些手指觸屏引發(fā)事件開始纷纫,到任何一個手指離開屏幕導致事件結束為止)枕扫。下面是相關的屬性和方法:?

// 事件類型?

@property(nonatomic,readonly) UIEventType type NS_AVAILABLE_IOS(3_0);?

@property(nonatomic,readonly) UIEventSubtype subtype NS_AVAILABLE_IOS(3_0);?

// 事件產生的時間?

@property(nonatomic,readonly) NSTimeInterval timestamp;?

// 觸摸對象的集合?

@property(nonatomic, readonly, nullable) NSSet*allTouches;?

// 獲取對應窗口上的觸摸對象的集合?

- (nullable NSSet*)touchesForWindow:(UIWindow *)window;?

// 獲取對應視圖上的觸摸對象的集合?

- (nullable NSSet*)touchesForView:(UIView *)view;?

// 獲取對應手勢識別器中的觸摸對象的集合?

- (nullable NSSet*)touchesForGestureRecognizer:(UIGestureRecognizer *)gesture NS_AVAILABLE_IOS(3_2);?

// - (nullable NSArray*)coalescedTouchesForTouch:(UITouch *)touch NS_AVAILABLE_IOS(9_0);?

- (nullable NSArray *)predictedTouchesForTouch:(UITouch *)touch NS_AVAILABLE_IOS(9_0);

2、UIGestureRecognizer

? ? ? UIGestureRecognizer 是專門針對 UIView 的手勢識別器辱魁,用于識別并處理特定手勢铡原,一個手勢包含一個或多個 UITouch。其派生體系如下:

二商叹、事件傳遞過程

? ? ? iOS是依靠“事件響應鏈”進行事件傳遞的燕刻,所謂“事件響應鏈”是一個由許多 UIResponder 對象組成的鏈條,要搞清楚這個鏈條剖笙,先來看看APP的視圖結構卵洗,如下圖:

? ? ? APP的生成次序是:首先生成?AppDelegate &?UIApplication,然后添加 UIWindow弥咪,再添加 rootViewController(UIViewController)过蹂,最后 rootViewController 的 view?添加各個子 view,這樣就形成一個視圖層級關系聚至,事件響應鏈就依賴這種關系酷勺,那么一條響應鏈的結構順序就是:

AppDelegate ->?UIApplication ->?UIWindow ->?UIViewController -> 根view -> 子view1 -> 孫子view1 ->>>

在這條響應鏈中,前一個對象依次是后一個對象的下一個響應者(即nextresponder)扳躬,它們之間的這種關系是在 setRootViewController &&?addsubview && ?viewcontroller初始化的時候形成的脆诉。

比如執(zhí)行方法: [viewA addsubview:viewB]; ?后,

就會有:viewB.nextResponder =?viewA贷币。

? ? ? 可見每一個視圖的父視圖是唯一的击胜,每一個視圖的下一個響應者也是唯一的,但是每一個父視圖就可以有好多子視圖役纹,可以有好多上一個響應者偶摔,因此一個APP可以有多條事件響應鏈,最終形成一個樹形結構促脉。

? ? ? 下面來看一條事件響應鏈的響應過程辰斋,以上圖為例,當一個事件(UIEvent)發(fā)生時瘸味,系統(tǒng)首先會將其傳遞給最上層的響應者(通常是處于視圖層級最頂部對應位置的視圖)宫仗,這個響應者就是一條響應鏈中的一個節(jié)點,如果該響應者能夠處理事件硫戈,則處理該事件并終止事件傳遞锰什,如果不能處理,事件就會沿著這條響應鏈向上(向AppDelegate那個方向)傳遞,直到遇到能夠處理該事件的響應者汁胆,最終如果事件未能被處理就會被丟棄梭姓。下面是具體步驟:

(1) 事件(UIEvent)發(fā)生。

(2) initial view 嘗試處理嫩码,如果不能處理誉尖,則傳遞給其父視圖(superview)。

(3) 父視圖嘗試處理铸题,如果不能處理铡恕,則繼續(xù)傳遞給其父視圖。

(4) 父視圖嘗試處理丢间,如果不能處理探熔,則傳遞給它所在的視圖控制器。

(5) 視圖控制器嘗試處理烘挫,如果仍不能處理诀艰,則將事件傳遞給?UIWindow。

(6) UIWindow?嘗試處理饮六,如果不能處理其垄,則傳遞給 UIApplication。

(7) UIApplication?嘗試處理卤橄,如果不能處理绿满,則傳遞給?AppDelegate。

(8) AppDelegate?嘗試處理窟扑,如果也不能處理喇颁,則丟棄該事件。

需要注意一點:當 UIView 添加了手勢識別器辜膝,事件傳遞到該 view 的時候无牵,那么該 view 的手勢識別器會優(yōu)先處理該事件,比如:UIButton 添加點擊事件和tap手勢厂抖,tap手勢優(yōu)先處理事件(另外,如果 UIButton 重寫 touchesBegan:withEvent: 等方法時克懊,addTarget:action:forControlEvents: 方法無效)忱辅,并且如果上層視圖沒有添加手勢識別器的話,下層視圖的手勢事件任然會執(zhí)行谭溉。

? ? ? 上面說過墙懂,一個APP可能有好多條事件響應鏈,那么事件發(fā)生時扮念,執(zhí)行哪一條呢损搬?這就要歸功于它的原理事件分發(fā)機制(hit-Testing)。當手指觸摸屏幕時,UIKit 就會產生一個事件(UIEvent)對象巧勤,并把它添加到事件隊列中嵌灰,然后由 UIApplication 從事件隊列中取出首先分發(fā)給?UIWindow,從?UIWindow 開始進行分發(fā)颅悉,分發(fā)是依靠 UIView 的兩個分類方法?hitTest:withEvent: &?pointInside:withEvent: 以及?UIView?的?userInteractionEnabled沽瞭、hidden、alpha 屬性來進行的剩瓶,以下圖為例:

假如觸摸事件發(fā)生在 ViewE 區(qū)域內驹溃,事件分發(fā)機制將執(zhí)行如下步驟:

(1) 從 window 開始一直傳遞到 ViewA,此過程和下面過程一樣延曙,不作詳細說明豌鹤。

(2) ViewA 調用?hitTest:withEvent: 方法,該方法內部做了一些判斷枝缔,先檢查 ViewA 的 userInteractionEnabled=YES布疙、hidden=NO、alpha>0.01 三個條件是否滿足要求魂仍,然后調用 pointInside:withEvent: 方法判斷觸摸點是否在該 UIView 的區(qū)域內拐辽,當所有檢查合格后,事件分發(fā)給它的子視圖?ViewB &?ViewC擦酌,也就是 ViewB &?ViewC?調用?hitTest:withEvent: 方法俱诸。

(3) 假如 ViewB 在 ViewC 的頂部,那么?ViewB 先執(zhí)步驟(2)赊舶,不滿足要求睁搭,那么 ViewC 繼續(xù)執(zhí)步驟(2),滿足笼平,然后 ViewC?將事件分發(fā)給其子視圖?ViewD &?ViewE园骆,如果?ViewC 在 ViewB 的頂部,那么?ViewC 先執(zhí)行步驟(2)寓调,因滿足要求锌唾,固?ViewB 就會被忽略,不再執(zhí)行步驟(2)夺英。

(4)?ViewD 和?ViewE 的執(zhí)行步驟同步驟(3)晌涕,最后因為?ViewE 沒有子視圖,所以就成為第一響應者了痛悯。

點擊這里獲取驗證代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末余黎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子载萌,更是在濱河造成了極大的恐慌惧财,老刑警劉巖巡扇,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異垮衷,居然都是意外死亡厅翔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門帘靡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來知给,“玉大人,你說我怎么就攤上這事描姚∩” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵轩勘,是天一觀的道長筒扒。 經常有香客問我,道長绊寻,這世上最難降的妖魔是什么花墩? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮澄步,結果婚禮上冰蘑,老公的妹妹穿的比我還像新娘。我一直安慰自己村缸,他們只是感情好祠肥,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著梯皿,像睡著了一般仇箱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上东羹,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天剂桥,我揣著相機與錄音,去河邊找鬼属提。 笑死权逗,一個胖子當著我的面吹牛,可吹牛的內容都是我干的冤议。 我是一名探鬼主播旬迹,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼求类!你這毒婦竟也來了?” 一聲冷哼從身側響起屹耐,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤尸疆,失蹤者是張志新(化名)和其女友劉穎椿猎,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體寿弱,經...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡犯眠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了症革。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筐咧。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖噪矛,靈堂內的尸體忽然破棺而出量蕊,到底是詐尸還是另有隱情,我是刑警寧澤艇挨,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布残炮,位于F島的核電站,受9級特大地震影響缩滨,放射性物質發(fā)生泄漏势就。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一脉漏、第九天 我趴在偏房一處隱蔽的房頂上張望苞冯。 院中可真熱鬧,春花似錦侧巨、人聲如沸舅锄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巧娱。三九已至,卻和暖如春烘贴,著一層夾襖步出監(jiān)牢的瞬間禁添,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工桨踪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留老翘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓锻离,卻偏偏與公主長得像铺峭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子汽纠,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內容