當(dāng)你設(shè)計(jì)你的應(yīng)用程序時(shí)嗡靡,你可能需要?jiǎng)討B(tài)的響應(yīng)事件焚刺。例如匪蝙,一個(gè)觸摸事件可以由屏幕上的很多對(duì)象產(chǎn)生主籍,你必須決定由哪個(gè)對(duì)象響應(yīng)對(duì)應(yīng)的事件和明白該對(duì)象如何接收到事件的。
當(dāng)用戶產(chǎn)生了一個(gè)事件逛球,UIKit創(chuàng)建了一個(gè)包含處理事件所需信息的事件對(duì)象千元。然后把這個(gè)事件對(duì)象放置在全局事件隊(duì)列。對(duì)于觸摸事件颤绕,會(huì)被包裝成一個(gè)UIEvent對(duì)象幸海。對(duì)于動(dòng)作事件,會(huì)根據(jù)你所使用的框架和你感興趣的動(dòng)作事件而有所不同奥务。
一個(gè)事件沿著一個(gè)具體的路徑向前傳遞物独,直到有一個(gè)對(duì)象可以對(duì)其進(jìn)行處理。首先氯葬,
UIApplication單例對(duì)象會(huì)從事件隊(duì)列里獲取一個(gè)事件并把它分發(fā)挡篓,一貫地將事件發(fā)送到應(yīng)用的key window對(duì)象,key window對(duì)象會(huì)把事件傳遞到原始對(duì)象進(jìn)行處理帚称,原始對(duì)象會(huì)根據(jù)事件的類型而有所不同官研。
· 觸摸事件。對(duì)于觸摸事件闯睹,window對(duì)象首先會(huì)嘗試傳遞事件到產(chǎn)生觸摸事件的視圖戏羽。這個(gè)視圖被稱為hit-test視圖,尋找hit-test視圖的過程被稱為hit-testing楼吃。
· 動(dòng)作和遠(yuǎn)程控制事件始花。window對(duì)象會(huì)把搖一搖動(dòng)作或者遠(yuǎn)程控制事件發(fā)送到第一響應(yīng)者進(jìn)行處理。
這些事件路徑的最終目標(biāo)是尋找一個(gè)對(duì)象可以處理和相應(yīng)一個(gè)事件所刀。因此,UIKit首先會(huì)事件發(fā)送到最適合處理該事件的對(duì)象捞挥。最適合處理觸摸事件的對(duì)象就是hit-test視圖浮创,對(duì)于其他事件,對(duì)象就是第一響應(yīng)者砌函。接下來的部分會(huì)詳細(xì)展開hit-test視圖和第一響應(yīng)者對(duì)象是如何被確認(rèn)的斩披。
Hit-Testing返回觸摸事件發(fā)生的視圖
iOS利用hit-tesing尋找被觸摸的視圖溜族。hit-tesing需要檢查一個(gè)觸摸事件是否在相關(guān)的視圖對(duì)象的范圍內(nèi),如果是垦沉,它會(huì)遞歸地檢查這個(gè)視圖對(duì)象的所有子視圖煌抒。處于視圖層級(jí)中包含觸摸點(diǎn)的最底層視圖就會(huì)成為hit-test視圖。當(dāng)iOS確定hit-test視圖后厕倍,它會(huì)把觸摸事件拋給這個(gè)視圖去處理寡壮。
例如,假如用戶觸摸視圖E(圖2-1)讹弯。iOS通過以下順序從子視圖中找出hit-test視圖
1.觸摸點(diǎn)在視圖A里面况既,所以會(huì)檢查子視圖B和C。
2.觸摸點(diǎn)不在視圖B的范圍內(nèi)组民,但是在視圖C的范圍內(nèi)棒仍,所以它會(huì)檢查子視圖D和E。
3.觸摸點(diǎn)不在視圖D臭胜,但在視圖E的范圍內(nèi)莫其。
視圖E是在視圖層級(jí)中包含觸摸點(diǎn)的最底層視圖,所以它就成為了hit-test視圖
(圖2-1)
(hitTest:withEvent:)方法返回一個(gè)給定CGPoint和UIEvent的hit-test視圖耸三。(hitTest:withEvent:)方法通過(pointInside:withEvent:)方法調(diào)用乱陡。如果一個(gè)點(diǎn)通過(hitTest:withEvent:)方法判斷是在視圖的范圍內(nèi),(pointInside:withEvent:)方法就會(huì)返回YES吕晌。然后這個(gè)方法就會(huì)在返回YES的子視圖上面遞歸地調(diào)用(hitTest:withEvent:)方法蛋褥。
如果這個(gè)點(diǎn)通過(hitTest:withEvent:)方法判斷不在視圖的范圍內(nèi),最先調(diào)用的(pointInside:withEvent:)方法就會(huì)返回NO睛驳。這個(gè)點(diǎn)被忽略了烙心,(hitTest:withEvent:)方法就會(huì)返回nil。如果一個(gè)子視圖返回NO乏沸,那么在視圖層級(jí)中整個(gè)分支都會(huì)被忽略淫茵,因?yàn)槿绻@個(gè)觸摸點(diǎn)不在這個(gè)視圖中產(chǎn)生,那么也不會(huì)再這個(gè)視圖的子視圖中產(chǎn)生蹬跃。這就意味著一些在父視圖外的子視圖的點(diǎn)不能接收到觸摸事件匙瘪,因?yàn)橛|摸點(diǎn)必須在父視圖和子視圖的范圍內(nèi)。子視圖的范圍超出父視圖的情況會(huì)發(fā)生在子視圖的clipsToBounds屬性被設(shè)為NO蝶缀。
注意:一個(gè)觸摸對(duì)象的生命周期會(huì)跟它的hit-test密切關(guān)聯(lián)丹喻,即使后來觸摸移到view的外面。
hit-test視圖有第一次機(jī)會(huì)去處理一個(gè)觸摸事件翁都。如果hit-test視圖不能處理一個(gè)事件碍论,這個(gè)事件就會(huì)在視圖的響應(yīng)者鏈條上傳遞,直到系統(tǒng)找到一個(gè)可以找到該事件的對(duì)象柄慰。
響應(yīng)者鏈條由響應(yīng)者對(duì)象組成
很多類型的事件依賴一個(gè)響應(yīng)者鏈條進(jìn)行傳遞鳍悠。響應(yīng)者鏈條是一系列的響應(yīng)者對(duì)象税娜。由第一個(gè)響應(yīng)者開始,并由application對(duì)象結(jié)束藏研。如果第一響應(yīng)者不能處理一個(gè)事件敬矩,事件會(huì)向前傳遞給響應(yīng)者鏈條的下一個(gè)響應(yīng)者。
響應(yīng)者對(duì)象是一個(gè)可以處理事件的對(duì)象蠢挡。UIResponder類是所有響應(yīng)者對(duì)象的基類弧岳,它定義的程序接口不只是為了事件的處理,而且為了普通的響應(yīng)者行為袒哥。UIApplication,UIViewController和UIView的實(shí)例都可以成為響應(yīng)者缩筛,意味著所有的視圖和很多的關(guān)鍵控制器對(duì)象都可以是響應(yīng)者。注意:核心動(dòng)畫圖層都不可以成為響應(yīng)者堡称。
首先由第一響應(yīng)者接收事件瞎抛。一般的,第一項(xiàng)響應(yīng)者是一個(gè)視圖對(duì)象却紧。一個(gè)對(duì)象通過以下兩種方式成為第一響應(yīng)者:
1桐臊、復(fù)寫(canBecomeFirstResponder)方法并返回YES。
2晓殊、接收一個(gè)(becomFirstResponder)信息断凶。如果有必要,一個(gè)對(duì)象可以發(fā)送這條信息給它自己巫俺。
注意:一個(gè)對(duì)象成為第一響應(yīng)者前確保應(yīng)用已經(jīng)把這個(gè)對(duì)象的圖層渲染出來认烁。例如,我們一般在(viewDidAppear:)方法里面調(diào)用(becomeFirstResponder)方法介汹,如果嘗試在(viewWillAppear:)方法里面分配第一響應(yīng)者却嗡,對(duì)象的圖層還沒有渲染出來,所以(becomeFirstResponder)方法就會(huì)返回NO嘹承。
事件不是唯一的依賴響應(yīng)者鏈條的對(duì)象窗价。響應(yīng)者鏈條被用于以下的情況:
· 觸摸事件。如果hit-test視圖不能處理一個(gè)觸摸事件叹卷,那么這個(gè)事件就會(huì)從hit-test視圖開始沿著響應(yīng)者鏈條傳遞撼港。
· 動(dòng)作事件。通過UIKit來處理搖一搖動(dòng)作事件骤竹,第一響應(yīng)者必須實(shí)現(xiàn)UIResponder類的(motionBegan:withEvent:)方法或者(motionEnded:withEvent:)方法之一帝牡。
· 遠(yuǎn)程控制事件。第一響應(yīng)者處理遠(yuǎn)程控制事件必須實(shí)現(xiàn)UIResponder類的(remoteControlReceivedWithEvent:)方法蒙揣。
· 動(dòng)作信息靶溜。