前言ide
事件處理完整過程;學習
1,在手指觸摸屏幕時,會產生一個事件,系統會把這個事件添加到UIApplication管理的事件隊列中。
2,取出隊列中最前面的事件,交給主窗口Window。
3,主窗口會逐級向上來尋找最適合的視圖控件view。
4,找到最適合的view後,這個view就是最適合的響應者。
5,若是這個最適合的響應者不響應事件,那麼它就會把這個事件交給它的父控件來響應。
6,若是都不對這個事件做出響應的話,最後仍是交給UIApplication來丟棄這個事件。code
關鍵的是兩個方法;隊列
(1)- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;查找最合適的響應視圖;事件
(2)- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ return YES;
};判斷點擊位置是否在當前的視圖內;在的話在查找子視圖,不在跳轉至同級視圖或者是父視圖。get
(一)UIButton是一個比較特殊的UI控件,相對於通常的view,它能夠便可以使用target-action添加事件,也可使用addGestureRecognizer添加手勢並執行,可是你會發現,二者共存的時候,點擊UIButton會優先執行手勢的事件,而不會再執行target-action的事件,也就是說UIButton執行完手勢事件後,會中斷響應鏈。it
TestButton *testBtn = [[TestButton alloc] initWithFrame:CGRectMake(50, 50, 50, 30)]; testBtn.backgroundColor = [UIColor blueColor]; [testBtn addTarget:nil action:@selector(clickButton) forControlEvents:UIControlEventTouchUpInside]; [viewB addSubview:testBtn]; UITapGestureRecognizer *buttonTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickTap:)]; [testBtn addGestureRecognizer:buttonTap]; - (void)clickButton { NSLog(@"button click"); } - (void)clickTap:(UIGestureRecognizer *)ges { NSLog(@"button ges %@",ges); }
上述的代碼,點擊testBtn,會執行clickTap:這個方法;即執行了手勢代碼。這是爲何呢,其實我也不是很明白,下面我總結我查到的資料。io
(1)有的人說手勢事件和target-action事件是在UIButton的UITouch委託方法中執行- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event或者是- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event,在手勢事件不爲nil的狀況下,優先執行手勢。可是我重寫了UIButton的UITouch的委託方法,若不執行[super touchesEnded:touches withEvent:event];的狀況下target-action事件不會執行反之就執行,而手勢無論怎樣都會執行。因此這樣的狀況下我只能說,系統在二者共存的狀況下會執行手勢。event
(2)還看到一種說法就是,手勢是一系列的UIEvent事件,每次在傳遞響應事件,都先將事件傳遞給在事件鏈頂端的手勢,沒有識別就傳遞至下一個響應者的手勢,如都不識別,回到起點開始查找可以執行的touch事件。這樣的話,貌似就能夠說得通,咱們就當上面UIButton的taget-action的實現方式是在- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event或者是- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event這些委託事件中,在UIButton設置了手勢能夠被識別,就會放棄查找touch事件鏈,從而響應手勢事件。手勢未被識別,就查找touch事件鏈,執行target-action事件。table
(二)總結一下
事件的響應原理:
1.調用最合適控件的touches.... 方法;
2.若是調用了[super touches....];就會將事件順着響應者鏈條往上傳遞,傳遞給上一個響應者;
3.接着就會調 上一個響應者的touches.... 方法;
重寫view的touch委託事件,能夠解決一寫子視圖超出父視圖後,點擊部分位置沒法響應事件的等等問題。學習事件響應鏈能夠寫出很讚的效果;好比UIApplication的sendEvent:方法配合NSNotification能夠有意想不到的效果。
個人總結是,手勢會優先的截獲touch事件,若手勢識別失敗,則會進入touch事件的- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event委託方法,若識別成功,則執行手勢並會進入touch事件的- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event來取消本次的touch事件,事件響應鏈停止,不少的手勢與touch事件衝突好比說UITableViewCell的- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath與手勢共存點擊失效的問題,就是這樣引發的,touch事件響應因手勢執行成功而被停止。解決的方法就是:重寫UIGestureRecognizerDelegate中的
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch方法。
#pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { // 輸出點擊的view的類名 NSLog(@"%@", NSStringFromClass([touch.view class])); // 若爲UITableViewCellContentView(即點擊了tableViewCell),則不截獲Touch事件 if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) { return NO; } return YES; }
判斷點擊的視圖是否爲UITableViewCell,是的話手勢就不截獲touch事件;如不是則截獲touch事件。