iOS-事件傳遞,響應者鏈條及常見面試題

在iOS中只有繼承UIResponder的對象才能夠接收并處理事件,UIResponder 是所有響應對象的基類,在UIResponder類中定義了處理上述各種事件的接口。我們熟悉的 UIApplication、 UIViewController、 UIWindow 和所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder,所以它們的實例都是可以構成響應者鏈的響應者對象,首先我們通過一張圖來簡單了解一下事件的傳遞以及響應.


hl.png

1.響應者鏈條

響應者鏈條就是由多個響應者對象連接起來的鏈條,它的作用就是讓我們能夠清楚的看見每個響應者之間的聯(lián)系,并且可以讓一個時間多個對象處理.

2.響應過程

iOS系統(tǒng)檢測到手指觸摸(Touch)操作時會將其打包成一個UIEvent對象,并放入當前活動Application的事件隊列,單例的UIApplication會從事件隊列中取出觸摸事件并傳遞給單例的UIWindow來處理,UIWindow對象首先會使用hitTest:withEvent:方法尋找此次Touch操作初始點所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖(最合適來處理的控件),這個過程稱之為hit-test view。

那么什么是最適合來處理事件的控件?
1.自己能響應觸摸事件
2.觸摸點在自己身上
3.從后往前遞歸遍歷子控件, 重復上兩步
4.如果沒有符合條件的子控件, 那么就自己最合適處理

3.兩個重要的響應方法(UIView的)

1.hit-test view:事件傳遞給控件的時候, 就會調用該方法,去尋找最合適的view并返回看可以響應的view

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.如果控件不允許與用用戶交互,那么返回nil
    if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES){
        return nil;
    }
    // 2. 如果點擊的點在不在當前控件中,返回nil
    if (![self pointInside:point withEvent:event]){
        return nil;
    }
    // 3.從后往前遍歷每一個子控件
    for(int i = (int)self.subviews.count - 1 ; i >= 0 ;i--){
        // 3.1獲取一個子控件
        UIView *childView = self.subviews[i];
        // 3.2當前觸摸點的坐標轉換為相對于子控件觸摸點的坐標
        CGPoint childP = [self convertPoint:point toView:childView];
        // 3.3判斷是否在在子控件中找到了更合適的子控件(遞歸循環(huán))
        UIView *fitView = [childView hitTest:childP withEvent:event];
        // 3.4如果找到了就返回
        if (fitView) {
            return fitView;
        }
    }
    // 4.沒找到,表示沒有比自己更合適的view,返回自己
    return self;
}
  1. pointInside: 該方法判斷觸摸點是否在控件身上,是則返回YES,否則返回NO,point參數(shù)必須是方法調用者的坐標系.
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    return NO;
}

4.涉及的相關面試題

1.事件的傳遞方向: 事件傳遞是從上自下傳遞,響應是從下到上,所謂的上就是父視圖而已,也就是離窗口最近的.

2.穿透控件:
2.1 如果我們不想讓某個視圖響應事件,只需要重載 PointInside:withEvent:方法,讓此方法返回NO就行了.
2.2 若是view上有view1,view1上有view2,點擊view2,view2自己響應,點擊view1,view1不響應,只有view響應,也就是隔層傳遞

/*
 重載view1的此方法,如果點在自己身上,且子控件中有最合適的響應者,就返回對應子控件,否則就不響應,并將該事件隨著響應者鏈條往回傳遞,交給上一個響應者來處理. (即調用super的touches方法)
 
 誰是上一個響應者?
 1. 如果view的控制器存在,就傳遞給控制器;如果控制器不存在,則將其傳遞給它的父視圖
 2. 在視圖層次結構的最頂級視圖,如果也不能處理收到的事件或消息,則其將事件傳遞給window對象進行處理
 3. 如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象
 4. 如果UIApplication也不能處理該事件或消息,則將其丟棄
*/
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    
    CGRect frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    BOOL value = (CGRectContainsPoint(frame, point));
    NSArray *views = [self subviews];
    for (UIView *subview in views) {
        value = (CGRectContainsPoint(subview.frame, point));
        if (value) {
            return value;
        }
    }
    return NO;
}

例如放大控件響應區(qū)域,view上有n個子視圖,點擊其中一個讓另一個來響應等等,都是可以通過重載pointInside來達到目的.

如有問題,歡迎指正分享~
.End

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容