事件傳遞機制
- 當(dāng)iOS程序中發(fā)生觸摸事件后山宾,系統(tǒng)會將事件加入到UIApplication管理的一個任務(wù)隊列中
- UIApplication將處于任務(wù)隊列最前端的事件向下分發(fā)崇众。即UIWindow彻消。
- UIWindow將事件向下分發(fā)葛超,即UIView颁井。
- UIView首先看自己是否能處理事件淫奔,觸摸點是否在自己身上。如果能振定,那么繼續(xù)尋找子視圖必怜。
- 遍歷子控件,重復(fù)3,4兩步后频。
- 如果沒有找到棚赔,那么自己就是事件處理者。如果自己不能處理徘郭,那么不做任何處理靠益。
- 注意:其中 UIView不接受事件處理的情況主要有以下三種
- alpha <0.01
- userInteractionEnabled = NO
- hidden = YES
此外在遍歷子控件的時候是倒序遍歷,此目的是為了讓最上層添加的view最先響應(yīng)事件
整理整個流程如下:
UIApplication -> UIWindow -> 倒序遍歷子控件 ->找到能響應(yīng)的view -> 倒序遍歷子控件 ->....->找到最終響應(yīng)的view
事件傳遞主要依賴的是以下兩個函數(shù):
hitTest:(CGPoint)point withEvent:(UIEvent *)event
pointInSide:(CGPoint)point withEvent:(UIEvent *)event
如果想要自行更改響應(yīng)視圖残揉,可以重寫hitTest:withEvent
方法胧后,指定要響應(yīng)事件的view即可
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"-----%@",self.nextResponder.class);
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) return nil;
//判斷點在不在這個視圖里
if ([self pointInside:point withEvent:event]) {
//在這個視圖 遍歷該視圖的子視圖
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
//轉(zhuǎn)換坐標到子視圖
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
//遞歸調(diào)用hitTest:withEvent繼續(xù)判斷
UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
if (hitTestView) {
//在這里打印self.class可以看到遞歸返回的順序。
return hitTestView;
}
}
//這里就是該視圖沒有子視圖了 點在該視圖中抱环,所以直接返回本身壳快,上面的hitTestView就是這個纸巷。
NSLog(@"命中的view:%@",self.class);
return self;
}
//不在這個視圖直接返回nil
return nil;
}
下面示例是定義一個button的可點擊區(qū)域
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"%@ -- pointInside",self.class);
CGRect bounds = self.bounds;
//若原熱區(qū)小于200x200,則放大熱區(qū)眶痰,否則保持原大小不變
//一般熱區(qū)范圍為40x40 瘤旨,此處200是為了便于檢測
CGFloat widthDelta = MAX(200 - bounds.size.width, 0);
CGFloat heightDelta = MAX(200 - bounds.size.height, 0);
bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
return CGRectContainsPoint(bounds, point);
}
響應(yīng)機制
響應(yīng)機制的順序和事件傳遞是反著的,事件傳遞是從上層往下層傳遞竖伯,像剝洋蔥一樣存哲,找到最終響應(yīng)事件的view,響應(yīng)機制則是從下層向上層七婴,響應(yīng)機制主要依賴的方法是:
touchesBegin 祟偷, touchesMoved, touchesEnded
等方法
響應(yīng)鏈是通過nextResponder屬性組成的一個鏈表。
點擊的view有 superView,nextResponder就是superView;
view.nextResponder.nextResponder是viewController 或者是 view.superView. view
view.nextResponder.nextResponder.nextResponder是 UIWindow (非嚴謹,便于理解)
view.nextResponder.nextResponder.nextResponder. nextResponder是UIApplication打厘、UIAppdelate修肠、直到nil (非嚴謹,便于理解)
touch事件就是根據(jù)響應(yīng)鏈的關(guān)系來層層調(diào)用(我們重寫touch 要記得 super 調(diào)用,不然響應(yīng)鏈會中斷)户盯。
比如我們監(jiān)聽self.view的touch事件嵌施,也是因為subviews的touch都在同一個響應(yīng)鏈里。
要注意的是莽鸭,UIControl吗伤, UIScrollView和手勢都可以阻斷響應(yīng)鏈,如果不想被阻斷蒋川,則可以重寫hitTest:withEvent
方法