iOS 事件響應鏈

iOS中三種事件類型

  • 觸屏事件(Touch Event)
  • 運動事件(Motion Event)
  • 遠端控制事件(Remote-Control-Event)

響應者對象(Responder Object)

響應者對象指的是有響應和處理上述3種事件能力的對象。響應者鏈就是由一系列響應者對象構成一個層次結構。
UIResponder是所有響應對象的基類,在UIResponder類中定義了處理上述各種事件的接口。我們熟悉的UIApplication、UIWindow、UIViewController、UIView都直接或間接繼承自UIResponder,所以它們都是可以構成響應者鏈的響應者對象。

響應鏈的傳遞

盜圖真香

這張圖清晰的解釋了響應鏈的傳遞過程:

  • 當發(fā)生觸屏事件后,系統(tǒng)會將事件加到UIApplication管理的一個任務隊列中,并將事件分發(fā)下去。
  • 通常先發(fā)送給keyWindow,UIWindow繼續(xù)在其視圖層次結構中找到一個最合適的視圖來處理事件。
  • UIWindow會在它視圖上調用hitTest:withEvent:方法,hitTest:withEvent又會調用自身的pointInside:方法,若返回YES,說明點擊區(qū)域在UIWindow范圍內,然后UIWindow遍歷它子視圖(后添加的子視圖先遍歷)調用hitTest:WithEvent:方法。
  • 上圖UIWindow遍歷子視圖MainViewMainView調用自身hitTest:withEvent方法,且pointInside:方法返回YES,繼續(xù)遍歷子視圖ViewC
  • ViewC調用自身hitTest:withEvent:方法,結果發(fā)現(xiàn)pointInside:方法返回NO,hitTest:方法返回nil;輪到ViewB
  • ViewB調用自身hitTest:withEvent:方法,結果發(fā)現(xiàn)pointInside:方法返回YES,繼續(xù)遍歷子視圖ViewB.2 ViewB.1
  • 遍歷到ViewB.1無子視圖可以遍歷,遍歷終止,hitTest:方法中返回自身即ViewB.1。
  • 到此響應鏈結束,ViewB.1響應了事件。

模擬系統(tǒng)的調用過程

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    if (self.userInteractionEnabled
        && !self.hidden
        && [self pointInside:point withEvent:event]) {
        // 使用reverseObjectEnumerator進行倒序遍歷
        for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
            // 將像素point從view中轉換到當前視圖中,返回在當前視圖中的像素值
            CGPoint convertedPoint = [subview convertPoint:point fromView:self];
            UIView *responseView = [subview hitTest:convertedPoint withEvent:event];
            if (responseView) {
                return responseView;
            }
        }
        //無子視圖返回自身
        return self;
    }
    return nil;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    //判斷點擊位置是否在視圖范圍內
    if (CGRectContainsPoint(self.bounds, point)) {
        return YES;
    }
    return NO;
}

解決實際問題

1、響應超出父視圖外區(qū)域的事件

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    if (CGRectContainsPoint(self.bounds, point)) {
        return YES;
    }
    for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
        // 將像素point從view中轉換到當前視圖中,返回在當前視圖中的像素值
        CGPoint convertedPoint = [subview convertPoint:point fromView:self];
        BOOL inside = [subview pointInside:convertedPoint withEvent:event];
        if (inside) {
            return YES;
        }
    }
    return NO;
}

2、面試題superView上添加viewA,viewA上添加viewB,viewB上添加viewC,且B、C都不在各自視圖內。此時重寫viewB的pointInside:方法并返回YES,點擊A和點擊B分別響應哪個視圖的事件。

    [viewSuper addSubview:viewA];
    [viewA addSubview:viewB];
    [viewB addSubview:viewC];

    UITapGestureRecognizer *tapA = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapA:)];
    [viewA addGestureRecognizer:tapA];
    UITapGestureRecognizer *tapB = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapB:)];
    [viewB addGestureRecognizer:tapB];
    UITapGestureRecognizer *tapC = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapC:)];
    [viewC addGestureRecognizer:tapC];
    UITapGestureRecognizer *tapS = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapS:)];
    [viewSuper addGestureRecognizer:tapS];
    - (void)tapA:(UITapGestureRecognizer *)tap {
      NSLog(@"tapA");
    }

    - (void)tapB:(UITapGestureRecognizer *)tap {
      NSLog(@"tapB");
    }

    - (void)tapC:(UITapGestureRecognizer *)tap {
      NSLog(@"tapC");
    }

    - (void)tapS:(UITapGestureRecognizer *)tap {
      NSLog(@"tapSuper");
    }
  
1616056611363.jpg

答:點擊A,響應事件B,打印tapB
解:

  • 先viewSuper調用hitTest:方法并且pointInside:返回YES;
  • 遍歷子視圖ViewA,ViewA調用hitTest:并且點在范圍內pointInside:返回YES;
  • 遍歷子視圖ViewB,ViewB調用hitTest:雖然點不在范圍內,但pointInside:返回YES;
  • 接著遍歷ViewC,點擊的點不在ViewC范圍內 pointInside:返回NO;
  • ViewB的hitTest:返回自身;所以響應了事件B;
    答:點擊B,響應事件Super,打印tapSuper
    解:
  • 先viewSuper調用hitTest:方法并且pointInside:返回YES;
  • 遍歷子視圖ViewA,因為ViewB上的點不在ViewA范圍內,所以pointInside:返回NO;
  • viewSuper的hitTest:返回自身;所以響應了事件Super;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容