iOS-事件處理與如何獲得最佳點(diǎn)擊的View

GitHub Demo:https://github.com/shaozhe-chen/ResponderTest

首先我先提出三個(gè)問(wèn)題:

1、點(diǎn)擊屏幕如何找到最合適的view來(lái)響應(yīng)事件?

2、pointInside:withEvent:先調(diào)用?還是hitTest:withEvent:?還是touchBegin:withEvent:?

3、如何確定響應(yīng)鏈?

????大家平時(shí)在做UI開(kāi)發(fā)的時(shí)候,我估計(jì)大家都很少關(guān)注這些問(wèn)題的,我也是在一次面試中發(fā)現(xiàn)了自己的不足,所以趕緊研究一番,查漏補(bǔ)缺。


?????在正式介紹之前,先理解一下pointInside:withEvent和hitTest:withEvent:

?????pointInside:withEvent:判斷觸摸點(diǎn)是否落在視圖內(nèi),則return YES,否則為NO。

?????hitTest:withEvent:是返回一個(gè)view,則表示這個(gè)view是最合適響應(yīng)事件的,如果返回nil,則表示這個(gè)視圖不是最適合響應(yīng)的視圖。


一、點(diǎn)擊屏幕如何找到最合適的view響應(yīng)事件?

? ? 以下我不考慮不接收用戶交互的三種情況:userInteractionEnabled = NO,當(dāng)一個(gè)控件隱藏時(shí)?Hidden = YES, 當(dāng)一個(gè)控件為透明時(shí)alpha?<= 0.01時(shí)。

view

????首先先來(lái)看看這張圖,布局如上圖所示:控制器根視圖View上有一個(gè)藍(lán)色的子視圖A,子視圖A上有兩個(gè)子視圖C和B,D區(qū)域表示C跟B重合的部分,且B視圖在上面,C視圖在下面。

? ? 當(dāng)點(diǎn)擊B視圖時(shí),先執(zhí)行pointInside:withEvent:方法,執(zhí)行順序是:UIWindow -> 控制器根視圖 -> 藍(lán)色視圖A -> 紅色視圖B(如果點(diǎn)擊的是C視圖:執(zhí)行順序是:UIWindow -> 控制器根視圖 -> 藍(lán)色視圖A -> 紅色視圖B ->紅色視圖C) 。大家有沒(méi)有注意到,為什么沒(méi)有調(diào)用C的pointInside:withEvent:?原因是當(dāng)遍歷子視圖的時(shí)候,有多個(gè)子視圖,會(huì)優(yōu)先遍歷后加入的視圖,而點(diǎn)落在B視圖上,那么pointInside:withEvent:的調(diào)用就結(jié)束了,不會(huì)繼續(xù)調(diào)用C的pointInside:withEvent:方法了(這就是為什么點(diǎn)擊D區(qū)域,點(diǎn)都是落在C和B的bounds的范圍內(nèi),但是響應(yīng)的卻是B視圖)。

????當(dāng)找到B視圖之后,就會(huì)調(diào)用B的?hitTest:withEvent:,并返回B視圖。由于視圖的遍歷采用了遞歸的形式,所以?hitTest:withEvent:的調(diào)用順序是:B視圖 -> A視圖 -> 控制器根視圖View ->UIWindow。并且?hitTest:withEvent:都是返回B視圖。

? ??到此是否就能確定B視圖是最適合是響應(yīng)視圖呢?答案是否!

? ? 原因是,如果視圖是延伸到狀態(tài)欄底下的呢?我實(shí)際上點(diǎn)的是狀態(tài)欄,所以狀態(tài)欄才是最適合的響應(yīng)視圖,所以系統(tǒng)必須確認(rèn),這個(gè)點(diǎn)是否在狀態(tài)欄上。遍歷過(guò)程如果所示:

遍歷狀態(tài)欄

? ? 當(dāng)確定了點(diǎn)不在狀態(tài)欄之后,系統(tǒng)還會(huì)繼續(xù)從從UIWindow到B視圖開(kāi)始遍歷一次,只是此次遍歷不會(huì)再遍歷狀態(tài)欄??偟谋闅v過(guò)程可以壓縮為:UIWindow到B視圖 -> UIStatusBarWindow到到UIStatusBarItem?->?UIWindow到B視圖

????當(dāng)然了,如果點(diǎn)確實(shí)是落在了狀態(tài)欄上,遍歷會(huì)不一樣:UIWindow到B視圖 -> UIStatusBarWindow到UIStatusBarItem?->?UIWindow到B視圖 -> UIStatusBarWindow到UIStatusBarItem?->?UIWindow到B視圖

? ? 到這里,就已經(jīng)完完全全確定了最合適的響應(yīng)視圖了。接下來(lái)可以回答是

二、pointInside:withEvent:先調(diào)用?還是hitTest:withEvent:?還是touchBegin:withEvent:?

????根據(jù)上面的分析,我們知道是通過(guò)先調(diào)用pointInside:withEvent:確定點(diǎn)是否落在該視圖上,如果是,才在該視圖上遍歷該視圖的子視圖,直到成功遍歷到最合適的視圖之后,才會(huì)調(diào)用hitTest:withEvent:返回該視圖作為最適合響應(yīng)的視圖。至于touchBegin:withEvent:,當(dāng)然是在確定完適合響應(yīng)的視圖之后,才會(huì)調(diào)用的。

三、如何確定響應(yīng)鏈

? ? 當(dāng)我們成功確定過(guò)了最適合響應(yīng)的視圖B之后,響應(yīng)鏈的確定就容易得多了,不需要再像尋找視圖遞歸遍歷那么麻煩了。而是直接就根據(jù)視圖遞歸遍歷的結(jié)果(打印nextResponder):視圖B -> 視圖A - > 控制器根視圖View --> UIWindow --> UIApplication --> AppDelegate。當(dāng)然了,UIApplication , AppDelegate并沒(méi)有在尋找最適合的響應(yīng)視圖的遍歷流程中,原因是底層的事件,首先是傳給AppDelegate,AppDelegate把事件交給UIApplication,通過(guò)UIApplication 的window把事件交給window。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容