在我們使用iOS app的時(shí)候,在界面上這里點(diǎn)一下那里拖一下,app也接收我們的手勢從而調(diào)用相對應(yīng)的方法。那么這篇文章就是講解當(dāng)我們點(diǎn)擊界面的時(shí)候,iOS是如何知道我們點(diǎn)擊的是哪一個(gè)View?
那么這個(gè)過程就是由hit-testing來完成的。通過hit-testing app 可以知道由那個(gè) view 來響應(yīng)事件。
下面我就簡單介紹一下 hit-testing 是怎么運(yùn)作的。當(dāng)我們在界面發(fā)生觸碰等手勢的時(shí)候,UIKit 就會打包出一個(gè) UIEvent 對象,并且會把這個(gè)對象傳遞給當(dāng)前正在活躍的 app ,分發(fā)給 app 后單利 UIApplication 就會從它的事件隊(duì)列里面取出一個(gè)事件進(jìn)行響應(yīng)。然后接下來 UIApplication 就要開始煩惱要哪個(gè) View 來響應(yīng)這個(gè)事件,那么這個(gè)時(shí)候就是 hit-testing 出場的時(shí)候了。
hit-testing 的執(zhí)行過程是:當(dāng) UIApplication 接到 UIEvent 以后就會將事件傳給 UIWindow ,然后 UIWindow 將事件傳給它的 SubView ,然后再次傳給 SubView 判斷是不是發(fā)生在這個(gè) View 里面,直到找到最小的發(fā)生這個(gè)事件的 View 。如果沒有找到就返回自身,然后從兄弟 View 又開始找。 但是問題來了 hit-testing 是以什么順序找 SubView 的呢。就是你添加 SubView 的逆序來遍歷的,換句話說就是從最頂層的 SubView 開始找。
如圖說明:

我添加 View 的順序是:
[self.view addSubView:View1];
[self.view addSubView:View2];
[self.view addSubView:View3];
所以 hit-testing 檢測 SubView 的順序是:

所以 hit-testing 進(jìn)行的是深度優(yōu)先的檢測,當(dāng)然也不是無腦的深度優(yōu)先,假如現(xiàn)在 View3 有自己的 SubView 那么 hit-testing 是不會檢測 View3 的 SubView 的。因?yàn)樵跈z測 View3 的時(shí)候就已經(jīng)可以斷定事件發(fā)生的點(diǎn)不在 View3 內(nèi)所以它的 SubView 也是不會檢測的。所以 hit-testing 在檢測的時(shí)候還是會進(jìn)行進(jìn)行剪枝的從而提高效率。
在知道了 hit-testing 的檢測過程以后在代碼里面是怎么實(shí)現(xiàn)的呢?當(dāng)然我們不可能準(zhǔn)確的知道只能是猜測大概的情況。
在 UIView 中有兩個(gè)方法分別是:
- (BOOL)pointInside:(CGPoint)*point* withEvent:(UIEvent *)*event;
- (UIView *)hitTest:(CGPoint)*point* withEvent:(UIEvent *)*event;
hit-testing 就是調(diào)用
- (UIView *)hitTest:(CGPoint)*point* withEvent:(UIEvent *)*event;
來得到發(fā)生事件的最小的 UIView ,而就是通過調(diào)用
- (BOOL)pointInside:(CGPoint)*point* withEvent:(UIEvent *)*event;
來判斷一個(gè)事件是否發(fā)生在一個(gè) UIView 中。所以過程應(yīng)該是 UIView 在接受到 hit-testing 消息后,先是判斷自身的 alpha 、userInteractionEnabled、hidden 等屬性,如果這些屬性不滿足要求那么
- (UIView *)hitTest:(CGPoint)*point* withEvent:(UIEvent *)*event;
直接返回 nill ,如果符合要求就掉用
- (BOOL)pointInside:(CGPoint)*point* withEvent:(UIEvent *)*event;
判斷事件是否發(fā)生在自己這里,如果不在自己這里,就返回 nill ,如果是在自己這里那么就對自己的 SubView 調(diào)用
- (UIView *)hitTest:(CGPoint)*point* withEvent:(UIEvent *)*event;
從而得到一個(gè) View 并且返回。
那么到這里為止 hit-testing 的具體過程就講完了,那么知道了有什么好處呢?在 UIView 的子類中我們可以重寫
- (UIView *)hitTest:(CGPoint)*point* withEvent:(UIEvent *)*event;
所以有趣的事情就多了。