iOS-事件響應鏈的學習

前言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事件。

相關文章
相關標籤/搜索