First Steps
Tips1:Understanding Event Handling, Responders, and the Responder Chain
- 概述:Apps 接受和響應(yīng)事件均使用 UIResponder 對(duì)象,它的子類包括 UIView, UIViewController, UIApplication 。一個(gè) responder 類接受原生的事件數(shù)據(jù)然后將它傳遞給其他的 responder 對(duì)象。當(dāng)你的 app 接受到了一個(gè)事件, UIKit 將會(huì)自動(dòng)的將這個(gè)事件發(fā)送給最可能接受的對(duì)象 ——
first responder。它沒(méi)有能 handle 的事件將會(huì)通過(guò)響應(yīng)鏈傳遞給一個(gè)又一個(gè)的 responder,這些都是在你的 app 中動(dòng)態(tài)配置的。在你的 app 中,不止有一條響應(yīng)鏈。 UIKit 默認(rèn)定義了對(duì)象如何在 responder 之間傳遞的規(guī)則,不過(guò)你可以通過(guò)自己修改某些方法的方式來(lái)完成對(duì)這一特性的修改,定制自己的傳遞規(guī)則。當(dāng)界面上有一個(gè) textfield 兩個(gè)背景 view 時(shí),首先如果這個(gè) textfield 無(wú)法 handle 某一事件,它將會(huì)被傳遞給它的父視圖上去。在 rootView 上,它將會(huì)把這個(gè)事件傳遞給它自己的 viewController, 然后才會(huì)繼續(xù)傳遞給 UIWindow。如果 UIWindow 還無(wú)法 handle 這個(gè)事件,它將會(huì)被傳遞給 UIApplication 對(duì)象,當(dāng)這個(gè)對(duì)象時(shí) UIResponder 的實(shí)例并且也不在響應(yīng)鏈中,這個(gè)事件也有可能會(huì)被傳遞給 UIApplicationDelegate 。responderChain.png
-
為一個(gè)事件選擇第一響應(yīng)者:對(duì)于每一種事件來(lái)說(shuō), UIKit 都會(huì)先選擇某一種 firstResponder 然后先把事件傳遞給它。
-
Touch event:它的第一響應(yīng)者是當(dāng) touch 動(dòng)作發(fā)生時(shí)所在的 view -
Press event:它的第一響應(yīng)者是 -
Shake-motion event/RemoteControl event/EdtingMenu event:它的第一響應(yīng)者是你指定的 firstResponder - 需要注意的是:motion 事件和螺旋器,加速儀等有關(guān)系,它并不 follow 響應(yīng)鏈。
- 控件類直接與他們的 target object 使用 action messages 發(fā)送消息。也就是說(shuō),當(dāng)一個(gè)用戶與一個(gè) UIControl 類進(jìn)行交互時(shí),它將把這個(gè)動(dòng)作消息發(fā)送給它的目標(biāo)對(duì)象。實(shí)際上 UIControl 類也能享受到響應(yīng)鏈的優(yōu)點(diǎn),因?yàn)楫?dāng)一個(gè)控件并沒(méi)有設(shè)置它的 target action 時(shí), UIKit 會(huì)開(kāi)始沿著當(dāng)前 UIControl 對(duì)象的響應(yīng)鏈尋找一個(gè)可能接受這個(gè) action method 的對(duì)象。例如: UIKit 的 edting menu 就是用這個(gè)特性去尋找可以實(shí)現(xiàn) cut,copy 等方法的對(duì)象。
- 如果一個(gè) view 添加了一個(gè)手勢(shì),這個(gè)手勢(shì)會(huì)首先接受 touch 和 press 事件,只有當(dāng)所有的手勢(shì)都無(wú)法接受這個(gè)事件后,這個(gè)事件才會(huì)被傳遞給這個(gè) view 去 handle。
-
-
決定哪一個(gè)響應(yīng)者包含一個(gè) Touch 事件:UIKit 使用
hitTest:withEvent:方法去判斷當(dāng)前 touch 事件是在哪里發(fā)生的。 UIKit 會(huì)在繼承鏈中比較這個(gè) touch location 和 view 對(duì)象的 bounds。hitTest:withEvent:方法會(huì)穿越 UIView 的 view 繼承鏈,然后尋找最底下包含這個(gè) touch 事件的子視圖,這個(gè) view 就會(huì)成為這個(gè) touch 事件的 firstResponder- 如果一個(gè) touch 事件的位置在 view 的 bounds 外,
hitTest:withEvent:方法就會(huì)忽略這個(gè) view 和它的所有子視圖。當(dāng)一個(gè) view 的 clipToBounds 屬性設(shè)置為 NO 時(shí),表示并不裁剪這個(gè) view 的子視圖。那么如果這個(gè) view 的 subViews 如果在這個(gè) view 的 bounds 外,即使這個(gè) subView 接收到了 touch 事件,也不會(huì)去處理的。也就是說(shuō),hitTest:withEvent:方法已經(jīng)自動(dòng)把這個(gè) subView 過(guò)濾掉了。 - 當(dāng)?shù)谝淮伟l(fā)生了這個(gè) touch 事件時(shí), UIKit 會(huì)創(chuàng)建 UITouch 對(duì)象,然后只有等到這個(gè) touch 事件結(jié)束時(shí)才會(huì)釋放這個(gè) touch 對(duì)象。當(dāng) touch 的 location 或者其他的參數(shù)改變時(shí), UIKit 會(huì)自動(dòng)的更新 UITouch 對(duì)象的信息。唯一不會(huì)改變的屬性為這個(gè) touch 事件的 containing view。即使這個(gè) touch 事件的位置有可能會(huì)移出它一開(kāi)始的 origin containing view 的范圍,這個(gè) touch view 的屬性也不會(huì)改變。
- 如果一個(gè) touch 事件的位置在 view 的 bounds 外,
-
改變響應(yīng)鏈:你可以通過(guò)重寫(xiě)
nextResponder屬性來(lái)改變響應(yīng)鏈。當(dāng)你這樣做時(shí), nextResponder 對(duì)象就是你返回的對(duì)象。許多 UIKit 類已經(jīng)改寫(xiě)了這個(gè)屬性- UIView 對(duì)象:如果這個(gè) view 是一個(gè) viewController 的 rootView,那么它的 nextResponder 就是這個(gè) viewController,否則,這個(gè) view 的 nextResponder 是它的 superView
- UIViewController 對(duì)象:如果這個(gè) viewController 是被另外一個(gè) viewController present 過(guò)來(lái)的,那么他的 nextResponder 就是 present 它的那個(gè) viewController
- UIWindow 對(duì)象:它的 nextResponder為 UIApplication 對(duì)象
- UIApplication 對(duì)象:只有當(dāng)一個(gè) app delegate 對(duì)象是 UIResponder,并且它不是一個(gè) view,viewController或者 app 自身時(shí),UIApplication對(duì)象的 nextResponder 才為 app delegate 對(duì)象
Tips2:class UIResponder
- 概述:responder 對(duì)象是 UIResponder 類的實(shí)例,他們?cè)谝粋€(gè) UIKit app 中組成了事件處理的支柱。很多 key object 也是 responder 對(duì)象。包括 UIApplication 對(duì)象, UIViewCOntroller 對(duì)象,和 UIView 對(duì)象(包括 UIWindow 對(duì)象)。當(dāng)事件發(fā)生時(shí),UIKit 將這些事件分發(fā)給你的 app 內(nèi)的 responder 對(duì)象來(lái)處理。
- 事件有許多種類,包括 touch 事件,motion 事件,remote-control 事件,和 press 事件。想要去 handle 這些事件對(duì)象,我們必須重寫(xiě)相應(yīng)的方法,比如說(shuō)我們?nèi)绻?handle 一個(gè) touch 事件,我們可以實(shí)現(xiàn)
touchesBegan:withEvent:,touchesMoved:withEvent:,touchesEnded:withEvent:, 和touchesCancelled:withEvent:方法。在 touch 的條件下,responder 對(duì)象使用這些 UIKit 提供的事件信息去追蹤 touches 的改變,并且也可以更新 app 的界面。 - Responder 對(duì)象處理 UIEvent 對(duì)象,也可以接受其他通過(guò) inputView 輸入的普通 input,系統(tǒng)的 keyboard 是一個(gè)最明顯的 inputView。當(dāng) user 點(diǎn)擊 textfield 和 textView 對(duì)象時(shí),那個(gè) view 就會(huì)成為 firstResponder 然后被展示在屏幕上。同理,你也可以創(chuàng)建自己的 custom input view 然后將它展示在屏幕上。如果想將一個(gè)普通的 input view 關(guān)聯(lián)為一個(gè) responder ,可以將那個(gè) view 通過(guò)設(shè)置為 inputView 屬性分配給 responder。
Tips3:class UIEvent
- 概述:Apps 可以接受很多類型的事件,包括 touch 事件, motion 事件,和 press 事件等。Touch 事件是最常見(jiàn)的事件,它會(huì)傳遞給 touch 最初發(fā)生的 view。 RemoteControl 事件使 responder 對(duì)象能夠接收一個(gè)從外部控制的事件,例如耳機(jī),所以它可以控制音頻和視頻。
- 一個(gè) touch event 對(duì)象包括很多 touches (即有很多手指同時(shí) touch)所以和 event 有一定的關(guān)系。一個(gè) touch event 對(duì)象可能包括一個(gè)或多個(gè) touches ,每個(gè) touch 都是一個(gè) UITouch 對(duì)象。當(dāng) touch event 發(fā)生時(shí),系統(tǒng)自動(dòng)的將它路由到適當(dāng)?shù)?responder,然后調(diào)用適當(dāng)?shù)姆椒ā@?
touchesBegan:withEvent:。然后 responder 接著使用這些 touches 去定義可能發(fā)生的動(dòng)作 - 在一個(gè)多點(diǎn)觸控過(guò)程做,UIKit 會(huì)復(fù)用同樣的 UIEvent 對(duì)象,所以你永遠(yuǎn)都不應(yīng)該去保存一個(gè) event 對(duì)象/一個(gè)對(duì)象從 event 返回。如果你需要保存一個(gè) responder 以外的數(shù)據(jù),你應(yīng)該去保存數(shù)據(jù),將 UITouch / UIEvent 對(duì)象的數(shù)據(jù)保存到本地?cái)?shù)據(jù)結(jié)構(gòu)中。
總結(jié):
- 此為 iOS 開(kāi)發(fā)中的 UIKit 響應(yīng)鏈部分的第一部分,翻譯 + 個(gè)人理解自蘋(píng)果官方文檔 Touches, Presses, and Gestures,如有理解錯(cuò)誤望海涵和指正。
