當(dāng)我們點(diǎn)擊屏幕時(shí)助琐,iPhone OS
獲取到了用戶"單擊"這一行為星岗,操作系統(tǒng)把包含這些點(diǎn)擊事件的信息封裝成 UITouch
和 UIEvent
形成的實(shí)例溶握,然后找到當(dāng)前運(yùn)行的程序酌壕,逐級(jí)找到能夠響應(yīng)這個(gè)事件的對(duì)象,知道沒有響應(yīng)者響應(yīng)衫画。這一尋找的過程稱為響應(yīng)鏈
毫炉。
響應(yīng)者
在 iOS 中,能夠響應(yīng)事件的對(duì)象都是 UIResponder 的子類對(duì)象削罩。
UIResponder 提供了用戶點(diǎn)擊的四個(gè)回調(diào)方法瞄勾,分別對(duì)應(yīng)點(diǎn)擊、移動(dòng)弥激、點(diǎn)擊結(jié)束和取消點(diǎn)擊进陡,其中取消點(diǎn)擊只有在程序強(qiáng)制退出或來電視才會(huì)調(diào)用。
響應(yīng)鏈傳遞
系統(tǒng)時(shí)怎么通過用戶點(diǎn)擊的位置找到處理點(diǎn)擊事件的 view 微服?
上文說過系統(tǒng)通過不斷查找下一個(gè)響應(yīng)者來響應(yīng)點(diǎn)擊事件趾疚,而所有的課加護(hù)空間都是 UIResponder 直接或者間接的子類,那么我們是否可以在這個(gè)類的頭文件中找到關(guān)鍵的屬性呢?正好存在這么一個(gè)方法 - (nullable UIResponder*)nextResponder;
盗蟆,通過方法名不難獲取當(dāng)前 view 的下一個(gè)響應(yīng)者戈二,那么我們重寫 touchesBegan
方法舒裤, 逐級(jí)獲取下一個(gè)響應(yīng)者喳资,直到?jīng)]有響應(yīng)者位置。
相關(guān)代碼如下:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIResponder * next = [self nextResponder];
NSMutableString * prefix = @"".mutableCopy;
while (next != nil) {
NSLog(@"%@%@", prefix, [next class]);
[prefix appendString: @"--"];
next = [next nextResponder];
}
}
運(yùn)行結(jié)果:
AView
--UIView
----ViewController
------UIWindow
--------UIApplication
----------AppDelegate
雖然結(jié)果非常有層次腾供,但是從系統(tǒng)逐級(jí)查找響應(yīng)者的角度上來說仆邓,這個(gè)輸出的順序是剛好相反的。為什么會(huì)出現(xiàn)這種問題呢伴鳖?我們可以看到輸出中存在一個(gè)ViewController類节值,說明UIViewController也是UIResponder的子類。但是我們可以發(fā)現(xiàn)榜聂,controller是一個(gè)view的管理者搞疗,即便它是響應(yīng)鏈的成員之一,但是按照邏輯來說须肆,控制器不應(yīng)該是系統(tǒng)查找對(duì)象之一匿乃,通過nextResponder方法查找的這個(gè)思路是不正確的。
后來在 UIView 中發(fā)現(xiàn)這兩個(gè)方法豌汇,分別返回 UIview 和 BooL 類型的方法:
一個(gè)方法是返回響應(yīng)點(diǎn)擊事件的對(duì)象幢炸,
另一個(gè)是根據(jù)點(diǎn)擊坐標(biāo)返回事件是否發(fā)生在本視圖內(nèi)。
響應(yīng)者棧:
所有響應(yīng)者都是在查找中返回可響應(yīng)點(diǎn)擊的視圖拒贱,因此可以推測(cè)宛徊,UIApplication 對(duì)象維護(hù)這自己的一個(gè)響應(yīng)者棧,當(dāng) pointInside:withEvent: 返回 YES 時(shí)候逻澳,響應(yīng)者入棧闸天。
棧頂作為最先響應(yīng)的對(duì)象,如果 AVView 不處理事件斜做,那么出棧苞氮,移交給 UIView,依次下去陨享,知道事件得到了處理或者到達(dá) APPDelegate 后依舊未響應(yīng)葱淳,事件被摒棄為止。