iOS手勢與響應(yīng)者鏈

這篇文章側(cè)重于講解手勢識別、手勢沖突、以及手勢與響應(yīng)鏈的關(guān)系。在處理多個(gè)手勢識別場景,很容易出現(xiàn)手勢沖突的問題,比如,我們需要對圖片進(jìn)行單擊、長按、旋轉(zhuǎn)、縮放、拖動、翻轉(zhuǎn)等操作的時(shí)候,我們需要使用各種手勢來完成相應(yīng)的操作,這些手勢組合在一起很容易出現(xiàn)手勢沖突,而且當(dāng)引入手勢識別的時(shí)候,有時(shí)候你還會發(fā)現(xiàn)響應(yīng)者鏈似乎不符合規(guī)律了,這些都是處理手勢識別容易遇到的。

手勢

手勢識別在 iOS 中非常重要,它極大地豐富了用戶與iOS程序的交互方式。同時(shí)iOS提供的手勢API非常簡潔,在使用時(shí)一般的步驟為:1)創(chuàng)建手勢實(shí)例對象,2)添加手勢到需要識別的UIView中,注意每個(gè)手勢只對應(yīng)一個(gè)UIView。在 iOS 系統(tǒng)中主要有以下常見的手勢:

UIPanGestureRecognizer(拖動)

UIPinchGestureRecognizer(捏合)

UIRotationGestureRecognizer(旋轉(zhuǎn))

UITapGestureRecognizer(點(diǎn)按)

UILongPressGestureRecognizer(長按)

?UISwipeGestureRecognizer(輕掃)

UIScreenEdgePanGestureRecognizer (邊緣手勢)

響應(yīng)鏈

響應(yīng)者鏈?zhǔn)窍到y(tǒng)尋找事件響應(yīng)者的路徑。該路徑開始于firstResponder結(jié)束于單例application。事件首先會讓firstResponder對象去處理,如果它無法處理則會向其nextResponder對象轉(zhuǎn)發(fā)事件。當(dāng)所有對象都無法處理事件后將最后轉(zhuǎn)發(fā)到application處并最終忽略該事件。在UIKit中,UIApplication、UIView、UIViewController這幾個(gè)類都是直接繼承自UIResponder類。因此UIKit中的視圖、控件、視圖控制器,以及我們自定義的視圖及視圖控制器都有響應(yīng)事件的能力。這些對象通常被稱為響應(yīng)對象,是響應(yīng)鏈中的一個(gè)節(jié)點(diǎn)。

響應(yīng)者鏈png

手勢沖突

  • 1、如果一個(gè)手勢A的識別部分是另一個(gè)手勢B的子部分時(shí),默認(rèn)情況下A就會先識別,B就無法識別了。我們可以指定某個(gè)手勢執(zhí)行的前提是另一個(gè)手勢失敗才會識別執(zhí)行,這樣控制手勢識別的響應(yīng)順序。
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
  • 2、如果同一視圖需要一次響應(yīng)多個(gè)手勢操作,可以實(shí)現(xiàn)下面的UIGestureRecognizerDelegate的代理方法,當(dāng)返回YES的時(shí)候,可以同時(shí)響應(yīng)多個(gè)手勢。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

同時(shí)響應(yīng)例子:

- (void)test1OnView:(UIView *)view
{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    tap.delegate = self;
    [view addGestureRecognizer:tap];

    UILongPressGestureRecognizer *press = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    press.delegate = self;
    [view addGestureRecognizer:press];
    
    UISwipeGestureRecognizer *swip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    swip.delegate = self;
    [view addGestureRecognizer:swip];
    
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    pan.delegate = self;
    [view addGestureRecognizer:pan];
    
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    rotation.delegate = self;
    [view addGestureRecognizer:rotation];
    
}

- (void)onGestureRecognizerTrigger:(UIGestureRecognizer *)gestureRecognizer
{
    NSLog(@"%@", [gestureRecognizer class]);
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

可以看到拖動的時(shí)候,還識別了輕掃手勢:

手勢識別[5246:277106] UIPanGestureRecognizer
手勢識別[5246:277106] UISwipeGestureRecognizer
手勢識別[5246:277106] UIPanGestureRecognizer
手勢識別[5246:277106] UIPanGestureRecognizer
手勢識別[5246:277106] UIPanGestureRecognizer
  • 3、父視圖如果有手勢需要識別,子視圖同樣有相似觸摸事件需要處理,這時(shí)候就可能產(chǎn)生沖突。我們可以實(shí)現(xiàn) UIGestureRecognizerDelegate 的以下代理方法來解決相關(guān)沖突:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

在UIView中添加UITableView,當(dāng)UIView需要識別輕敲手勢的時(shí)候,這時(shí)UITableViewCell 點(diǎn)擊便失效,我們通過實(shí)現(xiàn)- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch代理方法,讓 UITableViewCell 可以被點(diǎn)擊。類似地在UIScrollView中添加 UITableView、UICollectionView 時(shí),UITableViewCell不能響應(yīng)相關(guān)點(diǎn)擊的時(shí)候,也可以通過這種方法解決。

- (void)test2OnView:(UIView *)view
{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    tap.delegate = self;
    tap.delegate = self;
    [view addGestureRecognizer:tap];
    
    UITableView *tableView = [[UITableView alloc] initWithFrame:view.bounds];
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:TableViewCellReuseIdentifier];
    [view addSubview:tableView];
    tableView.delegate = self;
    tableView.dataSource = self;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 50;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableViewCellReuseIdentifier];
    cell.textLabel.text = [NSString stringWithFormat:@"row = %ld", indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"didSelectRowAt index = %ld", indexPath.row);
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isKindOfClass:[UITableView class]]) {
        return NO;
    }
    
    if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) {
        return NO;
    }
    
    return YES;
}

手勢與響應(yīng)者鏈

1、手勢與響應(yīng)者鏈有一些差別,觸摸事件首先會傳遞到手勢上,如果手勢識別成功,就會取消事件的繼續(xù)傳遞。如果手勢識別失敗,事件才會被響應(yīng)鏈處理。例子見 手勢沖突 的第三點(diǎn)。

2、對于 UIButton,UISwitch,UISegmentedControl,UIStepper、UIPageControl 進(jìn)行單擊操作,如果父視圖有輕敲手勢需要識別,依然會按照響應(yīng)鏈來處理,先響應(yīng)這些控件的單擊事件,這僅適用于與控件的默認(rèn)操作重疊的手勢識別。

- (void)test3OnView:(UIView *)view
{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    [view addGestureRecognizer:tap];
    
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 30)];
    button.backgroundColor = [UIColor orangeColor];
    [button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
    [view addSubview:button];
}

- (void)buttonTapped:(UIButton *)button
{
    NSLog(@"%@", [button class]);
}

輸出結(jié)果為:

手勢識別[6618:352925] UIButton

3、如果子視圖和父視圖都有手勢需要識別,則按照firstResponder從子視圖到父視圖傳遞。

- (void)test4OnView:(UIView *)view
{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger:)];
    [view addGestureRecognizer:tap];
    
    UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRecognizerTrigger1:)];
    UIView *button = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 30)];
    button.backgroundColor = [UIColor orangeColor];
    [button addGestureRecognizer:tap1];
    
    [view addSubview:button];
}

- (void)onGestureRecognizerTrigger1:(UIGestureRecognizer *)gestureRecognizer
{
    NSLog(@"%s", __func__);
}

輸出結(jié)果為:

手勢識別[6680:355254] -[ViewController onGestureRecognizerTrigger1:]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點(diǎn)擊了?糾結(jié)于如何實(shí)現(xiàn)這個(gè)奇葩響應(yīng)需求?亦或是...
    Lotheve閱讀 59,623評論 51 604
  • 在iOS開發(fā)中經(jīng)常會涉及到觸摸事件。本想自己總結(jié)一下,但是遇到了這篇文章,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 6,251評論 4 26
  • -- iOS事件全面解析 概覽 iPhone的成功很大一部分得益于它多點(diǎn)觸摸的強(qiáng)大功能,喬布斯讓人們認(rèn)識到手機(jī)其實(shí)...
    翹楚iOS9閱讀 3,228評論 0 13
  • 手勢識別器是附加到視圖的對象,將低級別事件處理代碼轉(zhuǎn)換為更高級別的操作,它允許視圖以控件執(zhí)行的方式響應(yīng)操作。 手勢...
    坤坤同學(xué)閱讀 4,289評論 0 9
  • 周末就睡到12點(diǎn)多,三餐也幾乎是外賣來解決。大學(xué)生活沒有高三時(shí)候所想象的那種熱情和興奮,有的只是每天的慵懶和度日,...
    Casil閱讀 619評論 0 0

友情鏈接更多精彩內(nèi)容