ios開發(fā)—事件處理與如何獲得最佳點擊的View

項目源碼下載

事件的產生與傳遞
  • 事件是如何產生與傳遞的?

    • 當發(fā)生觸摸事件后,系統會將該事件加入到一個由UIApplication管理的事件隊列中. UIApplication會從時間隊列中取出最前面的時間,并將事件分發(fā)下去以便處理.主窗口會在視圖層次結構中找到一個最合適的視圖來處理觸摸時間.
    • 觸摸時間的傳遞是從父控件傳遞到子控件的,如果一個父控件不能接收事件,那么他里面的子控件也不能接收.
  • 當一個控件不能接收時間時一般有以下幾種情況
    1.不接收用戶交互userInteractionEnabled = NO
    2.當一個控件隱藏時Hidden = YES
    3.當一個控件為透明白時

    注意:UIImageView以及它的子控件默認是不能接收觸摸事件的

事件的響應

用戶點擊屏幕產生的一個觸摸事件,經過一系列的傳遞過程后,會找到一個最適合的視圖來處理事件.找到最合適的視圖控件后,就會調用控件的touches方法來作具體的時間處理.touches的默認做法是將事件順著響應者鏈條向上傳遞,將事件交給上一個響應者處理

  • 什么是響應者鏈條?
    • 由多個響應者對象連接起來的鏈條
  • 什么是響應者對象?
    • 繼承了UIResponder的對象
  • 如何去尋找上一個響應者
    1.如果當前的View是控制器的View,那么控制器就是上一個響應者
    2.如果當前的View不是控制器的View,那么他的父控件就是上一個響應者
    3.在視圖層次結構的最頂級視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進行處理
    4.如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象
    5.如果UIApplication也不能處理該事件或消息,則將其丟棄
系統是如何尋找最合適的View

1.先判斷自己是否能接收觸摸事件
2.再判斷觸摸的當前點在不在自己身上
3.如果在自己身上,它會從后往前遍歷子控件,遍歷出每一個控件后,重啟前兩步
4.如果沒有符合條件的子控件,那么自身就是最合適的View

在尋找最合適View的過程中,系統會調用2個方法

//作用:尋找最適合的View
//什么時候調用:當事件傳遞給當前View時就會調用這個方法
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView *fitView = [super hitTest:point withEvent:event];
    NSLog(@"%@",fitView);
    return fitView;
}
//作用:判斷觸摸點在不在當前的View上.
//什么時候調用:在hitTest方法當中會自動調用這個方法.
//注意:point必須得要跟當前View同一個坐標系.
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    return YES;
}

那么hitTest: withEvent:方法底層是如何實現的呢?

    // 判斷自己能否接收事件
    if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01){
        return nil;
    }
    // 觸摸點在不在自己身上
    if ([self pointInside:point withEvent:event] == NO) {
        return nil;
    }
    // 從后往前遍歷自己的子控件(重復前面的兩個步驟)
    int count = (int)self.subviews.count;
    for (int i = count -1; i >= 0; i--) {
        UIView *childV = self.subviews[i];
        // point必須得要跟childV相同的坐標系.
        // 把point轉換childV坐標系上面的點
        CGPoint childP = [self convertPoint:point toView:childV];
        UIView *fitView = [childV hitTest:childP withEvent:event];
        if (fitView) {
            return fitView;
        } 
    }
    // 如果沒有符合條件的子控件,那么就自己最適合處理
    return self;

在開發(fā)中或多或少會需要一些特殊的點擊,這里有2個小例子供大家參考

  • 一個按鈕被一個半透明的View部分遮擋,需要點擊到按鈕的時候,按鈕始終響應
  • 一個View超出了父視圖的范圍,需要點擊超出范圍的View也有響應
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    //當觸摸點在按鈕上的時候,才讓按鈕去響應事件.
    //把當前點轉換成按鈕坐標系上的點.
    CGPoint btnP =  [self convertPoint:point toView:self.btn];
    if ( [self.btn pointInside:btnP withEvent:event]) {
        return self.btn;
    }else{
        return [super hitTest:point withEvent:event];
    }
}

總結:核心代碼思路都是相同的,將當前點轉換為坐標系上的點,具體代碼可以文章開頭的項目源碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容