網(wǎng)上很多關(guān)于這方面的文章腺办,但是決定還是如何使用響應(yīng)者和響應(yīng)者鏈出發(fā)迟蜜,然后來解釋一波兒义矛,首先推薦去看官方文章Using Responders and the Responder Chain to Handle Events
本篇是對(duì)文章的翻譯
概述
應(yīng)用程序使用響應(yīng)對(duì)象接收和處理事件。UIResponder
谊娇。類的任何實(shí)例對(duì)象都是響應(yīng)者类垫,公共子類包括UIView
,UIViewController
,UIApplication
,響應(yīng)者接收原始事件數(shù)據(jù)惹悄,并且必須處理事件或?qū)⑵滢D(zhuǎn)發(fā)給另一個(gè)響應(yīng)者對(duì)象。 當(dāng)您的應(yīng)用收到事件時(shí)肩钠,UIKit會(huì)自動(dòng)將該事件定向到最合適的響應(yīng)者對(duì)象泣港,稱為第一響應(yīng)者。未處理的事件從響應(yīng)者傳遞到活動(dòng)響應(yīng)者鏈中的響應(yīng)者价匠,這是應(yīng)用程序的響應(yīng)者對(duì)象的動(dòng)態(tài)配置当纱。 圖1顯示了應(yīng)用程序中的響應(yīng)者,其界面包含label踩窖,文本 text field坡氯,button兩個(gè)background views。 該圖還顯示了事件如何在響應(yīng)者鏈之后從一個(gè)響應(yīng)者移動(dòng)到下一個(gè)響應(yīng)者洋腮。
如果text field不處理事件箫柳,UIKit
會(huì)將事件發(fā)送到文本字段的父UIView對(duì)象,然后是window
的根視圖啥供。 從根視圖開始悯恍,響應(yīng)器鏈在將事件定向到窗口之前轉(zhuǎn)移到擁有的視圖控制器。 如果window
無法處理事件伙狐,UIKit會(huì)將事件傳遞給UIApplication
對(duì)象涮毫,如果該對(duì)象是UIResponder
的實(shí)例而不是響應(yīng)者鏈的一部分,則可能傳遞給app delegate贷屎。
確定事件的第一響應(yīng)者
UIKit
根據(jù)事件的類型將對(duì)象指定為事件的第一響應(yīng)者罢防。 事件類型包括:
- 觸摸事件(Touch events):第一響應(yīng)者是觸摸點(diǎn)所在的視圖。
- 按壓事件(Press events):第一響應(yīng)者是有焦點(diǎn)的響應(yīng)者唉侄。
- 搖晃運(yùn)動(dòng)事件(Shake-motion events):第一響應(yīng)者是由我們自己(或者UIKit)指定為第一響應(yīng)者的對(duì)象咒吐。
- 遠(yuǎn)程控制事件(Remote-control events):第一響應(yīng)者是由我們自己(或者UIKit)指定為第一響應(yīng)者的對(duì)象。
- 編輯菜單消息(Editing menu messages):第一響應(yīng)者是由我們自己(或者UIKit)指定為第一響應(yīng)者的對(duì)象。
注意:與加速計(jì)渤滞、陀螺儀和磁力計(jì)相關(guān)的運(yùn)動(dòng)事件不遵循響應(yīng)者鏈,
Core Motion
會(huì)將這些事件直接傳遞給我們指定的對(duì)象榴嗅。有關(guān)更多信息妄呕,可以參看Core Motion Framework。
控件使用動(dòng)作消息直接與其關(guān)聯(lián)的目標(biāo)對(duì)象進(jìn)行通信嗽测。 當(dāng)用戶與控件交互時(shí)绪励,控件會(huì)向其target
對(duì)象發(fā)送操作消息。 動(dòng)作消息不是事件唠粥,但它們?nèi)匀豢梢岳庙憫?yīng)者鏈疏魏。 當(dāng)控件的target
對(duì)象為nil時(shí),UIKit
從target
對(duì)象開始并遍歷響應(yīng)者鏈晤愧,直到找到實(shí)現(xiàn)相應(yīng)操作方法的對(duì)象大莫。 例如,UIKit
編輯菜單使用此行為來搜索響應(yīng)器對(duì)象官份,這些對(duì)象實(shí)現(xiàn)具有cut:只厘,copy:
或paste:
等名稱的方法。
手勢(shì)識(shí)別器在其視圖之前接收touch
和press
事件舅巷。 如果視圖的手勢(shì)識(shí)別器無法識(shí)別觸摸序列羔味,則UIKit
會(huì)將touch
發(fā)送到視圖。 如果視圖沒有處理touch
钠右,UIKit
會(huì)將它們傳遞給響應(yīng)者鏈赋元。 有關(guān)使用手勢(shì)識(shí)別器處理事件的更多信息,請(qǐng)參閱處理UIKit手勢(shì)飒房。
確定哪個(gè)響應(yīng)者包含touch
事件
UIKit
使用基于視圖的hit-testing
來確定$touch
事件發(fā)生的位置搁凸。 具體來說,UIKit
將touch
位置與視圖層次結(jié)構(gòu)中視圖對(duì)象的邊界進(jìn)行比較情屹。
UIView
的 hitTest:withEvent:
方法遍歷視圖層次結(jié)構(gòu)坪仇,查找包含指定觸摸(touch)的最深子視圖,該子視圖成為觸摸事件的第一個(gè)響應(yīng)者垃你。
觸摸發(fā)生時(shí)椅文,UIKit
會(huì)創(chuàng)建一個(gè)UITouch
對(duì)象并將其與視圖關(guān)聯(lián)。 當(dāng)觸摸位置或其他參數(shù)發(fā)生變化時(shí)惜颇,UIKit
會(huì)使用新信息更新相同的UITouch
對(duì)象皆刺。 唯一不改變的屬性是視圖。 (即使觸摸位置移動(dòng)到原始視圖之外凌摄,觸摸視圖屬性中的值也不會(huì)改變羡蛾。)觸摸結(jié)束時(shí),UIKi
t會(huì)釋放UITouch
對(duì)象锨亏。
hitTest:withEvent: 方法介紹
- 如果觸摸位置位于視圖邊界之外痴怨,則
hitTest:withEvent:
方法將忽略該視圖及其所有子視圖忙干。 因此,當(dāng)視圖的clipsToBounds
屬性為NO時(shí)浪藻,即使它們恰好包含觸摸捐迫,也不會(huì)返回該視圖邊界之外的子視圖。
2.hitTest:withEvent:
方法會(huì)遍歷當(dāng)前視圖層爱葵,并調(diào)用每個(gè)子視圖的pointInside:withEvent:
方法來判斷子視圖的邊界是否包含觸摸點(diǎn)施戴。如果pointInside:withEvent:
返回YES,則會(huì)同樣遍歷子視圖的視圖層萌丈,直到找到包含指定點(diǎn)的最上層的視圖赞哗。如果視圖不包含該觸摸點(diǎn),就忽略此視圖層次結(jié)構(gòu)的分支辆雾。因此我們可以覆override
hitTest:withEvent
以隱藏子視圖中的觸摸事件肪笋。 - 此方法忽略掉被隱藏,禁用用戶交互或alpha級(jí)別小于0.01的視圖對(duì)象度迂。在確定命中時(shí)涂乌,此方法不會(huì)考慮視圖的內(nèi)容。因此英岭,即使指定的點(diǎn)位于該視圖內(nèi)容的透明部分湾盒,仍然可以返回視圖。
位于接收者界限之外的點(diǎn)永遠(yuǎn)不會(huì)被報(bào)告為命中诅妹,即使它們實(shí)際上位于接收者的子視圖中罚勾。如果當(dāng)前視圖的clipsToBounds
屬性設(shè)置為NO并且受影響的子視圖超出視圖的邊界,hitTest:withEvent:
方法也不會(huì)返回命中了此視圖吭狡。
改變響應(yīng)者鏈
您可以通過覆蓋響應(yīng)程序?qū)ο蟮?code>nextResponder屬性來更改響應(yīng)程序鏈尖殃。 執(zhí)行此操作時(shí),下一個(gè)響應(yīng)者是您返回的對(duì)象划煮。
許多UIKit類已經(jīng)覆蓋此屬性并返回特定對(duì)象送丰,包括:
-
對(duì)象。 如果視圖是視圖控制器的根視圖弛秋,則下一個(gè)響應(yīng)者是視圖控制器; 否則器躏,下一個(gè)響應(yīng)者是視圖的父視圖。
-
UIViewController
對(duì)象蟹略。
如果視圖控制器的視圖是window的根視圖登失,則下一個(gè)響應(yīng)者是window object
。
如果視圖控制器由另一個(gè)視圖控制器呈現(xiàn)挖炬,則下一個(gè)響應(yīng)者是呈現(xiàn)視圖控制器揽浙。 -
UIWindow
對(duì)象。 窗口的下一個(gè)響應(yīng)者是UIApplication
對(duì)象。 -
UIApplication
對(duì)象馅巷。 下一個(gè)響應(yīng)者是UIApplication
膛虫,但僅當(dāng)應(yīng)用程序委托是UIResponder的實(shí)例且不是視圖,視圖控制器或應(yīng)用程序?qū)ο蟊旧頃r(shí)钓猬。