在講述UI事件傳遞以前,先要知道UIView 和 UILayer的區別是什麼.簡而言之bash
事件的傳遞主要和兩個方法有關ide
// 返回哪一個視圖響應這個事件,返回nil表示不處理該事件
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判斷某一個點擊的位置是否在視圖範圍內
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
複製代碼
接下來來看看這個流程圖 ui
首先,先判斷視圖的用戶交互是否打開、是否隱藏、透明度是否大於0.01,若是有一個不符合,那麼該視圖將不處理任何事件響應,也不會進行後續的操做了.若是這三個條件都知足了,就會調用當前視圖的-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
方法來判斷點擊的點是否在當前視圖範圍內,若是不在的話也會返回nil再由它當前視圖的父視圖去遍歷它的同級兄弟視圖,調用對應的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法.若是當前視圖的-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
返回YES,那麼就會以倒敘的方式遍歷當前視圖的子視圖,遍歷的過程當中會調用全部自視圖的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法,若是某個子視圖返回了最終的事件響應視圖的話,就會把對應的視圖做爲最終的響應視圖返回給調用方,若是返回的是nil的話就會繼續遍歷當前視圖的下一個子視圖.若是所有遍歷完了以後都沒有對應的子視圖去響應事件的話,因爲當前點擊位置在當前視圖範圍內,就會把當前的視圖做爲響應視圖返回給調用方.spa
在下圖這個正方形按鈕中,只有白色圓型區域能夠響應點擊事件. 設計
自定義一個button,經過- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
和
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
方法來實現上訴功能.
#import "CustomButton.h"
@implementation CustomButton
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (!self.userInteractionEnabled ||
[self isHidden] ||
self.alpha <= 0.01) {
return nil;
}
// 若是點擊區域在白色圓形區域內,則返回self,不然不響應事件
if ([self pointInside:point withEvent:event]) {
return self;
}
else{
return nil;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat x1 = point.x;
CGFloat y1 = point.y;
CGFloat x2 = self.frame.size.width / 2;
CGFloat y2 = self.frame.size.height / 2;
// 這就是傳說中的勾股定理
double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
if (dis <= self.frame.size.width / 2) {
return YES;
}
else{
return NO;
}
}
@end
複製代碼
好了,如今這個自定義的button就實現了上述的功能了. 回到最開始的問題,點擊那個白色位置,事件是如何進行傳遞的呢.以下圖: code
首先點擊位置在C二、B二、和A區域內,那麼這3個視圖都是有可能響應事件的.若是C2是B2的子視圖,B2是A的子視圖的話,那麼按照順序應該先調用子視圖的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法,而若是他們三個視圖是平級的話,按照同級視圖倒敘遍歷的方式,也是C2先響應(由於C2覆蓋在B2和A上).因此順序就是C2先執行-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法若是返回nil,B2再執行,最後纔是A.若是都沒有處理這個事件,那麼最後會傳到UIApplication,若是仍然沒有處理這個事件,那麼最後就會忽略這個事件.cdn