iOS - 事件傳遞鏈與響應(yīng)鏈

一、事件鏈

用戶點(diǎn)擊屏幕時(shí),首先 UIApplication 對(duì)象先收到該點(diǎn)擊事件狞尔,再依次傳遞給它上面的所有子 view,直到傳遞到最上層巩掺。即由系統(tǒng)向最上層 view 傳遞偏序,Application -> window -> root view -> sub view -> ... -> first view 即傳遞鏈。
反之胖替,由最基礎(chǔ)的 view 向系統(tǒng)傳遞研儒,first view -> super view -> ... -> view controller -> window -> Application -> AppDelegate 即響應(yīng)鏈豫缨。

簡單總結(jié),事件鏈包含傳遞鏈和響應(yīng)鏈端朵,事件通過傳遞鏈傳遞上去好芭,通過響應(yīng)鏈找到相應(yīng)的 UIResponse

二冲呢、誰來響應(yīng)事件 — 傳遞鏈

只有繼承了 UIResponser 的對(duì)象才能夠接受處理事件栓撞。UIResponse 是響應(yīng)對(duì)象的基類,定義了處理各種事件的接口碗硬。在 UIKit 中我們使用響應(yīng)者對(duì)象 Responder 接收和處理事件瓤湘。一個(gè)響應(yīng)者對(duì)象一般是 UIResponder 類的實(shí)例,它常見的子類包括 UIView恩尾,UIViewControllerUIApplication弛说,這意味著幾乎所有我們?nèi)粘J褂玫目丶际琼憫?yīng)者,如 UIButton翰意,UILabel 等等木人。

UIResponder 及其子類中,我們是通過有關(guān)觸摸 UITouch 的方法來處理和傳遞事件 UIEvent冀偶,具體的方法如下:

open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

UIResponder 還可以處理 UIPress醒第、加速計(jì)、遠(yuǎn)程控制事件进鸠,這里僅討論觸摸事件稠曼。
UITouch 內(nèi),存儲(chǔ)了大量觸摸相關(guān)的數(shù)據(jù)客年,當(dāng)手指在屏幕上移動(dòng)時(shí)霞幅,所對(duì)應(yīng)的 UITouch 數(shù)據(jù)也會(huì)更新,例如:
這個(gè)觸摸是在哪個(gè) window 或者哪個(gè) view 內(nèi)發(fā)生的量瓜?
當(dāng)前觸摸點(diǎn)的坐標(biāo)是司恳?
前一個(gè)觸摸點(diǎn)的坐標(biāo)是?
當(dāng)前觸摸事件的狀態(tài)是绍傲?

這些都存儲(chǔ)在 UITouch 里面扔傅。另外需要注意的是,在這四個(gè)方法的參數(shù)中烫饼,傳遞的是 UITouch 類型的一個(gè)集合 (而不是一個(gè) UITouch)猎塞,這對(duì)應(yīng)了兩根及以上手指觸摸同一個(gè)視圖的情況。

們以 UIView 來作為視圖層級(jí)的主要組成元素枫弟,便于理解邢享。但不止 UIView 可以響應(yīng)事件鹏往,實(shí)際只要是 UIResponder 的子類淡诗,都可以響應(yīng)和傳遞事件骇塘。


當(dāng)我們觸摸了屏幕。此時(shí)所擁有的信息是觸摸點(diǎn)的坐標(biāo)韩容,但無法直接知道用戶是想點(diǎn)哪個(gè)視圖款违。需要一個(gè)策略來找到這個(gè)第一響應(yīng)者,UIKit 為我們提供了命中測(cè)試 hit-testing 來確定觸摸事件的響應(yīng)者


以下為UIView不接受事件處理的情況:

view.hidden = YES;
view.userInteractionEnabled = NO;
view.alpha < 0.01;

具體流程如下:

  1. 用戶在點(diǎn)擊屏幕群凶;
  2. 系統(tǒng)將點(diǎn)擊事件加入到 UIApplication 管理的消息隊(duì)列中插爹;
  3. UIApplication 會(huì)從消息隊(duì)列中取出該事件傳遞給 UIWindow 對(duì)象;
  4. UIWindow 中調(diào)用方法 hitTest:withEvent: 请梢,在 hitTest:withEvent: 方法中調(diào)用 pointInside:withEvent: 來判斷當(dāng)前點(diǎn)擊的點(diǎn)是否在 UIWindow 內(nèi)部赠尾;
  5. 如若返回 yes,則倒序遍歷其子視圖找到最終響應(yīng)的子 view毅弧;
  6. 如果最終返回一個(gè) view气嫁,那么即為最終響應(yīng) view 并結(jié)束事件傳遞,如果無值返回則將 UIWindow 作為響應(yīng)者够坐。

其中核心方法如下:

// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   
// default returns YES if point is in bounds
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   
  • 方法 hitTest:withEvent: 用來獲取最終響應(yīng)事件的 view寸宵。
  • 方法 pointInside:withEvent:,用來判斷點(diǎn)擊的位置是否在視圖范圍內(nèi)元咙。

三梯影、怎樣傳遞事件 —— 響應(yīng)鏈

由離用戶最近的view向系統(tǒng)傳遞。如下所示:


圖中淺灰色的箭頭是指將 UIView 直接添加到 UIWindow 上情況庶香。

響應(yīng)鏈應(yīng)該是:ViewB -> ViewC -> ViewA -> UIViewController 對(duì)象 -> UIWindow 對(duì)象 -> UIApplication 對(duì)象 -> App Delegate

觸摸事件首先將會(huì)由第一響應(yīng)者響應(yīng)甲棍,觸發(fā)其 (target action) 等方法,根據(jù)觸摸的方式不同(如拖動(dòng)赶掖,雙指)救军,具體的方法和過程也不一樣。若第一響應(yīng)者在這個(gè)方法中不處理這個(gè)事件倘零,則會(huì)傳遞給響應(yīng)鏈中的下一個(gè)響應(yīng)者觸發(fā)該方法處理唱遭,若下一個(gè)也不處理,則以此類推傳遞下去呈驶。若到最后還沒有人響應(yīng)拷泽,則會(huì)被丟棄(比如一個(gè)誤觸)。

四袖瞻、完成流程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末司致,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子聋迎,更是在濱河造成了極大的恐慌脂矫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霉晕,死亡現(xiàn)場(chǎng)離奇詭異庭再,居然都是意外死亡捞奕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門拄轻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颅围,“玉大人,你說我怎么就攤上這事恨搓≡捍伲” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵斧抱,是天一觀的道長常拓。 經(jīng)常有香客問我,道長辉浦,這世上最難降的妖魔是什么墩邀? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮盏浙,結(jié)果婚禮上眉睹,老公的妹妹穿的比我還像新娘。我一直安慰自己废膘,他們只是感情好竹海,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丐黄,像睡著了一般斋配。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灌闺,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天艰争,我揣著相機(jī)與錄音,去河邊找鬼桂对。 笑死甩卓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蕉斜。 我是一名探鬼主播逾柿,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宅此!你這毒婦竟也來了机错?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤父腕,失蹤者是張志新(化名)和其女友劉穎弱匪,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體璧亮,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萧诫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年斥难,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片财搁。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蘸炸,死狀恐怖躬络,靈堂內(nèi)的尸體忽然破棺而出尖奔,到底是詐尸還是另有隱情,我是刑警寧澤穷当,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布提茁,位于F島的核電站,受9級(jí)特大地震影響馁菜,放射性物質(zhì)發(fā)生泄漏茴扁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一汪疮、第九天 我趴在偏房一處隱蔽的房頂上張望峭火。 院中可真熱鬧,春花似錦智嚷、人聲如沸卖丸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稍浆。三九已至,卻和暖如春猜嘱,著一層夾襖步出監(jiān)牢的瞬間衅枫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工朗伶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弦撩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓论皆,卻偏偏與公主長得像孤钦,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纯丸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容