摘要
觸摸事件的傳遞虱疏,事件的響應(yīng)
事件的產(chǎn)生
- 當(dāng)一個觸摸事件發(fā)生后,系統(tǒng)首先會把該事件加入一個待處理的事件隊列。UIApplication會首先處理最先加入隊列的事件擎椰。換句話說UIApplication是事件處理的第一個對象逻住。
- UIApplication將事件傳遞給keyWindow
- keyWindow從視圖層級中找到合適的事件處理視圖钟哥。
怎么在視圖層級中找到合適的視圖
view有三種情況不響應(yīng)觸摸事件。
- hiden
- userInteractionEnabled
- alpha < 0.01
找到合適視圖的具體過程
- 最頂層的視圖及keyWindow是否接受觸摸事件瞎访。
- 父視圖沒有hiden腻贰,userInteractionEnabled!=NO扒秸,alpha > 0.01并且觸摸區(qū)域在父視圖之內(nèi)播演。
- 如果父視圖中的subViews不為空,對subViews中的每個view執(zhí)行步驟2同樣的操作鸦采。
- 如果父視圖沒有subView或者subViews中的每個view都不能處理該觸摸事件宾巍,則父視圖是最終找到的view。
真?zhèn)€過程就是從最頂層的視圖開始通過遞歸找到最終的的合適的視圖渔伯。
hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
view使用hitTest檢測自己是不是應(yīng)該響應(yīng)改觸摸或者改event傳遞給子視圖去檢測顶霞。
里邊使用了遞歸。
下邊我們自己實現(xiàn)的一個重寫的hitTest方法锣吼。
#import "WYPResponderView.h"
@implementation WYPResponderView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//輸出tag
NSLog(@"******%ld****",self.tag);
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
return nil;
};
if ([self pointInside:point withEvent:event] == NO) return nil;
NSInteger count = self.subviews.count;
//從后往前遍歷子視圖
for (NSInteger i = count - 1; i >= 0; i--) {
UIView *childView = self.subviews[i];
CGPoint childP = [self convertPoint:point toView:childView];
UIView *foundView = [childView hitTest:childP withEvent:event];
if (foundView) {
return foundView;
}
}
return self;
}
@end
我們可以檢測下上述代碼的正確性并且跟不重寫hitTest方法的結(jié)果比較下选浑。
創(chuàng)建幾個視圖,并且給每個視圖設(shè)置不同的tag玄叠。
- (UIView *)generateView
{
WYPResponderView * mainView = [[WYPResponderView alloc] initWithFrame:CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT - 64)];
mainView.backgroundColor = [UIColor redColor];
mainView.tag = 0;
[self.view addSubview:mainView];
WYPResponderView *view1 = [[WYPResponderView alloc] initWithFrame:CGRectMake(20, 30, SCREEN_WIDTH - 80, 250)];
view1.backgroundColor = [UIColor greenColor];
view1.tag = 1;
[mainView addSubview:view1];
WYPResponderView *view2 = [[WYPResponderView alloc] initWithFrame:CGRectMake(20, 300, SCREEN_WIDTH - 80, 250)];
view2.backgroundColor = [UIColor blueColor];
[mainView addSubview:view2];
view2.tag = 2;
WYPResponderView *view1_1 = [[WYPResponderView alloc] initWithFrame:CGRectMake(10, 10, SCREEN_WIDTH - 80 - 20, 100)];
view1_1.backgroundColor = [UIColor grayColor];
view1_1.tag = 3;
[view1 addSubview:view1_1];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
button.userInteractionEnabled = NO;
[button addTarget:self action:@selector(XX) forControlEvents:UIControlEventTouchDown];
button.backgroundColor = [UIColor purpleColor];
[view1_1 addSubview:button];
WYPResponderView *view1_2 = [[WYPResponderView alloc] initWithFrame:CGRectMake(10, 130, SCREEN_WIDTH - 80 - 20, 100)];
view1_2.backgroundColor = [UIColor darkGrayColor];
view1_2.tag = 4;
[view1 addSubview:view1_2];
WYPResponderView *view2_1 = [[WYPResponderView alloc] initWithFrame:CGRectMake(10, 10, SCREEN_WIDTH - 80 - 20, 50)];
view2_1.backgroundColor = [UIColor grayColor];
view2_1.tag = 5;
[view2 addSubview:view2_1];
UIView *view2_2 = [[WYPResponderView alloc] initWithFrame:CGRectMake(10, 70, SCREEN_WIDTH - 80 - 20, 170)];
view2_2.backgroundColor = [UIColor yellowColor];
view2_2.tag = 6;
[view2 addSubview:view2_2];
UIView *view2_2_1 = [[UIView alloc] initWithFrame:CGRectMake(20, 20, SCREEN_WIDTH - 150, 100)];
view2_2_1.backgroundColor = [UIColor purpleColor];
view2_2_1.tag = 7;
[view2_2 addSubview:view2_2_1];
return mainView;
}
界面的最終呈現(xiàn)古徒。
點擊view2_2_1時的hitTest過程
如果我們修改hitTest方法如下,在默認(rèn)的hitTest方法上輸出一段內(nèi)容读恃。
再次點擊下view2_2_1
可以看到輸出跟我們重寫hitTest檢測過程是一樣的隧膘。
事件的響應(yīng)
可以用一張?zhí)O果的官方圖說明。
如果我們找到合適的view之后寺惫,則根據(jù)view的touchesBegan方法判斷view是否要處理該事件疹吃,如果不處理則將事件拋給下一個響應(yīng)者去處理。touchesBegan的默認(rèn)方法就是傳遞給下一個響應(yīng)者西雀。
如何找到下一個響應(yīng)者
- 如果當(dāng)前view是控制器的view萨驶,那么控制器就是上一個響應(yīng)者,事件就傳遞給控制器艇肴;如果當(dāng)前view不是控制器的view腔呜,那么父視圖就是當(dāng)前view的上一個響應(yīng)者叁温,事件就傳遞給它的父視圖
- 在視圖層次結(jié)構(gòu)的最頂級視圖,如果也不能處理收到的事件或消息核畴,則其將事件或消息傳遞給window對象進(jìn)行處理
- 如果window對象也不處理膝但,則其將事件或消息傳遞給UIApplication對象
- 如果UIApplication也不能處理該事件或消息,則將其丟棄