事件的產生和傳遞
- 發(fā)生觸摸事件后,系統(tǒng)會將該事件加入到一個由UIApplication管理的事件隊列中;
- UIApplication 會從事件隊列中取出最前面的事件,并將事件分發(fā)下去以便處理,通常,先發(fā)送事件給應用程序的主窗口keyWindow;
- 主窗口會在視圖層次結構中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理過程的第一步;
- 找到合適的視圖控件后,就會調用視圖控件的touches方法來作具體的事件處理。

UIView不接收觸摸事件的三種情況
- 不接收用戶交互 userInteractionEnabled = NO
- 隱藏 hidden = YES
- 透明 alpha = 0.0 ~ 0.01
提示: UIImageView 的 userInteractionEnabled默認就是NO,因此 UIImageView 以及它的子控件默認是不能接收觸摸事件的。
view中跟事件傳遞相關的三個方法
// 什么時候調用:當事件傳遞給當前view時,會調用當前view的hitTest方法。
// 作用:尋找最適合的view
// 返回值:返回誰,誰就是最適合的view,誰就響應事件,就會調用誰的touches方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1. 判斷自己能否接收事件
// 2. 點在不在自己身上
// 3. 從后往前遍歷自己的子控件,把事件傳遞給子控件,調用子控件的hitTest,
// 4. 如果子控件沒有找到最適合的view,那么自己就是最適合的view。
return [super hitTest:point withEvent: event];
}
// 作用:點在不在自己身上,調用當前view一個方法(pointInside:)
// 什么時候調用:在hitTest內部調用的
// 返回值:YES 在當前view身上,NO 不在
// point: 當前觸摸點
// 注意:必須得要跟方法調用者在同一個坐標系
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return YES;
}
// touchesBegan 方法的默認做法是將事件順著響應者鏈條向上傳遞,將事件交給上一個響應者進行處理
// 就會調用上一個響應者的touches方法
// 上一個響應者是誰?先看當前view是否為控制器的view,如果是,那么上一個響應者就是他所在的控制器,如果不是,上一個響應者就是他的父控件。
// 千辛萬苦找到你這個view,但是如果沒有處理,那么就按照默認來
- (void)touchesBegin:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@----touch", [self class]);
}
自己實現(xiàn)hitTest:withEvent:方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1. 判斷自己能否接收事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
// 不能接收事件
return nil;
}
// 2. 點在不在自己身上
if (![self pointInside:point withEvent: event]) {
return nil;
}
// 3. 從后往前遍歷自己的子控件,把事件傳遞給子控件,調用子控件的hitTest
int count = (int)self.subviews.count;
for (int i = count - 1; i >= 0; i--) {
// 獲取子控件
UIView *childView = self.subviews[i];
// 把當前點的坐標系轉換成子控件的坐標系
CGPoint childP = [self convertPoint: point toView: childView];
UIView *fitView = [childView hitTest:childP withEvent: event];
if (fitView) {
return fitView;
}
}
// 4. 如果子控件沒有找到最適合的view,那么自己就是最適合的view
return self;
}
傳遞和響應是兩個過程
完整過程如下
- 先將事件對象由上往下傳遞(由父控件傳遞給子控件),找到最合適的控件來處理這個事件;
- 調用最合適控件的touches...方法;
- 如果調用了[super touches...];就會將事件順著響應者鏈條往上傳遞,傳遞給上一個響應者;
- 接著就會調用上一個響應者的touches...方法
如何判斷上一個響應者
- 如果當前這個view是控制器的view,那么控制器就是上一個響應者;
- 如果當前這個view不是控制器的view,那么父控件就是上一個響應者。