?控件能接收事件的4個基本條件
(1) view.userInteractionEnabled == YES;
(2) view.hidden == NO;
(3) view.alpha > 0.01
(4) 該觸摸點是否落在該控件上
(5) hitTest它只負責找最合適的view來接受這個事件
(6) 如果要攔截事件必須實現(xiàn)touch方法,因為父類的默認處理是把事件拋給上一個響應者
接收(找到事件往下傳遞的終點,找到最佳響應者調用該返回的控件- touchBegan ... 等方法):
1. 用戶觸摸屏幕系統(tǒng)會產生一個UITouch對象,Event事件(里面包含UIEvent對象)
2. 然后把這次事件放進主運行循環(huán)的消息隊列中
3. 當UIAplication對象接收到這個事件的時候就會把這個事件交給UIWindow來處理
4. UIWindow會判斷它自身是否符合能接收事件的四個基本條件,如果不行則事件傳遞到此結束,否則會執(zhí)行以下步驟
5. UIWindow(UIWindow自身也實現(xiàn)這個方法了,如果它的子控件沒有合適的它的最終返回值為nil)會根據它子控件的數量從后往前遍歷
6. 系統(tǒng)判斷事件處理者的兩個重要的方法,我們可以重寫這兩個方法來自定義接收事件的最佳對象
/**
判斷一個View是否處理事件的最佳人選(如果父控件不能接收事件那么子控件肯定不能接收事件) :
注意 :
a. 如果想自己成為處理這個事件的最佳人選,阻斷事件往下傳播執(zhí)行以下兩步操作:
a1. 重寫 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
a2. 返回自身(注意判斷觸摸點是否落在該控件上)
b. 這個返回值是非常關鍵的,因為它決定事件的走向 和
決定誰來處理這個事件(調用該返回的控件- touchBegan ... 等方法)
1. 判斷它 userInteractionEnabled == YES hidden == NO alpha > 0.01
2. 判斷觸摸點是否落在該控件上(在該控件的坐標系中: x > 0 && y > 0)
3. 從后往前遍歷它的子控件,看它的子控件是否滿足以上條件
4. 如果它的子控件有一個滿足以上條件,則返回該子控件,否則返回自身
5. 如果它自身也不滿足 1 和 2 條件則返回空
*/
/**
* 從該View的層次結構中尋找最佳的事件接收者,如果沒有則返回nil
*
* @param point 在該view上的坐標系的點
* @param event 事件對象
*
* @return 最佳接收該事件的view nil 則沒有最佳接收該事件的人選,事件往上拋
*/
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
// 1. 判斷它 userInteractionEnabled == YES hidden == NO alpha > 0.01
if ( self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01 ) return nil;
// 2. 判斷觸摸點是否落在該控件上(在該控件的坐標系中: x > 0 && y > 0)
if ( [self pointInside:point withEvent:event] == NO ) return nil;
// 3. 從后往前遍歷它的子控件,看它的子控件是否滿足以上條件
NSInteger count = self.subviews.count;
for (NSInteger i = count - 1; i >= 0; i--) {
UIView *subView = self.subviews[i];
CGPoint subP = [self convertPoint:point toView:subView];
UIView *target = [subView hitTest:subP withEvent:event];
if ( target != nil ) {
return target;
}
}
// 如果子控件沒有合適人選,來到這里就足以證明控件自身滿足接收該事件的人選
return self;
}
/**
* 判斷觸摸點是否落在控件上(x > 0 && y > 0 && 點在該控件的范圍內)
*
* @param point 在該坐標系內的點
* @param event 觸摸事件對象
*
* @return YES 觸摸點落在該控件上 NO 觸摸點沒有落在該控件上
*/
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
// return [super pointInside:point withEvent:event];
// 模仿系統(tǒng)實現(xiàn)的方法
return CGRectContainsPoint(self.bounds, point);
}
響應
如果在事件接收過程中找到合適的人選了,但該人選沒有實現(xiàn)任何的 touch... 方法,那么系統(tǒng)默認會給我們默認實現(xiàn)以下事件相應流程 :
1. 系統(tǒng)會把事件逐層往上傳遞
2. 最終會傳遞到控制器的view(控制器的view會默認調用控制器的 touch... 方法),如果控制器沒有實現(xiàn)這些方法,事件會再次傳遞給UIWindow癌蚁,UIWindow會默認把這個事件銷毀,那么這次事件傳遞結束
3. 如果UIWindow的根控制器是導航控制器那么默認的事件傳遞會遵循以下步驟 :
1. 系統(tǒng)會把事件逐層往上傳遞
2. 最終會傳遞到導航控制器的當前控制器的view(控制器的view會默認調用控制器的 touch... 方法)江解,如果當前控制器沒有實現(xiàn)這些方法, 事件會傳遞給導航控制器的view然后會調用導航控制器的 touch 方法,如果導航控制器沒有實現(xiàn)這些方法,事件會再次傳遞給UIWindow铐伴,UIWindow會默認把這個事件銷毀,那么這次事件傳遞結束