原文地址:https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/understanding_event_handling_responders_and_the_responder_chain
如原作者發(fā)現(xiàn)有侵權(quán)行為可責(zé)令我在24小時(shí)之內(nèi)刪除摊溶,前提是你能看到杖狼。
翻譯者:毛毛可
學(xué)習(xí)事件是怎樣在你的app中傳遞的,并學(xué)會(huì)應(yīng)該如何處理它們.
Apps使用responder(響應(yīng)者對(duì)象)對(duì)象來(lái)接受并處理事件.一個(gè)responder對(duì)象可以是任意一個(gè)UIResponder類的實(shí)例,其中包括其子類UIView,UIViewController和UIApplication.
responder對(duì)象在接收到原始的事件對(duì)象,必須處理它或者將它繼續(xù)向前傳遞給下一個(gè)responder對(duì)象.當(dāng)你的app在接收到一個(gè)事件時(shí),UIKit框架會(huì)自動(dòng)的找到最合適的responder去處理該事件,這就是第一響應(yīng)者.
未處理的事件會(huì)在響應(yīng)者鏈中從這個(gè)responder傳遞給下一個(gè)responder.事實(shí)上你的app中沒(méi)有單獨(dú)的響應(yīng)者鏈.UIkit定義了responder傳遞的默認(rèn)規(guī)則,當(dāng)然你可以通過(guò)覆蓋responder對(duì)象中對(duì)應(yīng)的屬性來(lái)更改規(guī)則.
圖一 顯示了一條默認(rèn)的響應(yīng)者鏈,包括一個(gè)label,一個(gè)text field,一個(gè)button和兩個(gè)background view.如果text field不去處理事件,那么UIKit會(huì)將事件發(fā)送給textfield的父視圖對(duì)象.接著就是window的root view.從root view出來(lái),會(huì)順著響應(yīng)者鏈在到達(dá)window對(duì)象之前先傳遞給view controller.如果此時(shí)window也不處理此事件,UIKit會(huì)將事件分發(fā)給UIApplication對(duì)象,或者是分發(fā)給app delegate,app delegate是一個(gè)UIResponder實(shí)例,但此過(guò)程已經(jīng)不是響應(yīng)者鏈中的一部分了.
確定事件的第一響應(yīng)者
針對(duì)每種類型的事件,UIKit指定了第一響應(yīng)者,將事件發(fā)送給第一響應(yīng)者.第一響應(yīng)者的確定是基于事件的類型的.
Touch事件
第一響應(yīng)者就是觸摸發(fā)生的view.
Press事件
第一響應(yīng)者是注冊(cè)了focus的響應(yīng)者對(duì)象.
搖動(dòng)事件
第一響應(yīng)者是你指定的相應(yīng)對(duì)象.
遠(yuǎn)程控制事件
第一響應(yīng)者是你指定的相應(yīng)對(duì)象.
編輯菜單信息
第一響應(yīng)者是你指定的相應(yīng)對(duì)象.
Note
運(yùn)動(dòng)事件相關(guān)的加速,陀螺儀和磁力計(jì)都不會(huì)出現(xiàn)在響應(yīng)者鏈中.Core MOtion會(huì)派發(fā)這些事件給你對(duì)應(yīng)注冊(cè)的對(duì)象.
UIControl控件會(huì)直接通過(guò)相關(guān)聯(lián)的target-action傳送事件.
當(dāng)界面中的是UIControl控件時(shí),UIControl會(huì)調(diào)用target對(duì)象的action方法,或者說(shuō)返送一個(gè)action消息給其target對(duì)象.
action消息不是一個(gè)事件,但是依然可以利用響應(yīng)者鏈.當(dāng)UIControl對(duì)象的target為nil時(shí),UIKit開(kāi)始從target對(duì)象并順著響應(yīng)者鏈找,直到找到一個(gè)實(shí)現(xiàn)了相關(guān)action方法的對(duì)象.舉例,UIKit editing menu使用這種行為去搜索對(duì)應(yīng)responder對(duì)象的相關(guān)實(shí)現(xiàn)方法比如cut:
,copy:
或者paste:
.
如果在view中有附加的手勢(shì)識(shí)別器,手勢(shì)識(shí)別器會(huì)在在view之前接收到touch和press事件.如果view中所有的手勢(shì)識(shí)別都沒(méi)有識(shí)別成功,事件會(huì)傳給view去處理.如果此view沒(méi)有處理它們,UIKit會(huì)繼續(xù)傳給響應(yīng)者鏈的上一層.關(guān)于更多的手勢(shì)識(shí)別器處理事件,請(qǐng)參考UIKit Gestures.
確定哪個(gè)Responder包含Touch事件
UIKit使用 基于view的hit-testing方式去確定touch事件發(fā)生的位置.具體來(lái)說(shuō),UIKit會(huì)將touch的位置與在view層級(jí)中的view對(duì)象的容器范圍比較.UIView的hitTest:withEvent:
方法隨著view層級(jí),查找包含touch的最深層的子view.這個(gè)view將成為touch事件的第一響應(yīng)者.
Note
如果touch的位置超出了view的范圍,hitTest:withEvent:
方法會(huì)忽略此view及其所有子view.因此,當(dāng)view的clipsToBounds屬性為NO時(shí),即使包含了touch,超出view范圍的子view也不會(huì)有效.更多關(guān)于hitTest:withEvent:
方法的行為,請(qǐng)查看view的hitTest:withEvent:
方法.
UIkit總會(huì)賦值給view其范圍范圍的每個(gè)touch.當(dāng)觸摸發(fā)生時(shí),UIKit創(chuàng)建UITouch對(duì)象,直到觸摸結(jié)束后才釋放touch對(duì)象.如果觸摸的位置或者其他參數(shù)改變了,UIKit會(huì)及時(shí)更新UITouch對(duì)象的信息.只有一個(gè)屬性不可能發(fā)生改變就是是否在view范圍中.及時(shí)當(dāng)觸摸位置移出了原view,touch對(duì)象的屬性也不會(huì)改變.
改變響應(yīng)者鏈
你可以通過(guò)重載responder對(duì)象的nextResponder屬性來(lái)改變響應(yīng)者鏈.你可以在此方法(getter)返回下一個(gè)響應(yīng)者.
許多UIKit的類已經(jīng)重載了這個(gè)屬性,返回了指定的對(duì)象.
UIView對(duì)象,如果view是view controll的根視圖,那么view的下個(gè)響應(yīng)者就是view controller,否者下一個(gè)響應(yīng)者就是view的父視圖.
UIViewController對(duì)象.
如果一個(gè)vc是被另一個(gè)vcpresent出來(lái)的,那么vc的下一個(gè)響應(yīng)者就是present它出來(lái)的那個(gè)vc
如果window的根視圖是view controller.view,那么view controller的下一個(gè)響應(yīng)者就是window對(duì)象.
window對(duì)象.window的下一個(gè)responder是UIApplication對(duì)象.
UIApplication對(duì)象.下一個(gè)響應(yīng)者是app delegate.