天天看點

iOS hitTest與PointInside方法

作用:尋找最适合的View
    參數:目前手指所在的點.産生的事件
    傳回值:傳回誰, 誰就是最适合的View.
    什麼時候用調用:隻要一個事件,傳遞給一個控件時, 就會調用這個控件的hitTest方法
    -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

    作用:判斷point在不在方法調用者上
    point:必須是方法調用者的坐标系
    什麼時候調用:hitTest方法底層會調用這個方法,判斷點在不在控件上.
    -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
        return YES;
    }


    hitTest底層實作:

        判斷目前能不能接收事件
          if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= ) 
          return  nil;
        判斷觸摸點在不在目前的控件上
         if(![self pointInside:point withEvent:event]) return nil;
        從後往前周遊自己的子控件
        int count = (int)self.subviews.count;
        for (int i = count - ; i >= ;i-- ) {

        UIView *childV = self.subviews[i];
        把目前坐标系上的點轉換成子控件坐标系上的點.
        CGPoint childP = [self convertPoint:point toView:childV];
        判斷自己的子控件是不是最适合的View
        UIView *fitView = [childV hitTest:childP withEvent:event];
        如果子控件是最适拿的View,直接傳回
            if (fitView) {
                return  fitView;
            }
        }

        自己就是最适合的View
            return self.
           

hitTest使用場景一:

業務邏輯:
        底部一個按鈕, 按鈕的上面有一個View,遮擋在按鈕的上面.
        點選View時, View接收事件,當發現點選的點在按鈕的位置時, 讓底部的按鈕處理事件.

    實作思路:
        實作View的touchBegain方法,先堅聽UIView的點選.
        并去實作UIView的HitTest方法, 在hitTest方法當中通過把目前點轉換成按鈕所在的坐标系
            CGPoint btnP = [self convertPoint:point toView:self.btn];
        轉換過後檢視目前點在不在按鈕上,如果在按鈕上,就直接傳回按鈕.
        如果有在按鈕上,保持系統預設做法.

    實作代碼:
        -(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];
            }
        }
           

hitTest使用場景二:

業務邏輯: 
        按鈕可以随着手指拖動而拖動.拖動過程當中,按鈕當中的子控件也跟着拖動.
        讓超過按鈕的子控件也能夠響應事件,一般情況下,當一個控件超過他的父控件的時候,是不能夠接收事件的. 
        現在要做的事情就讓超過父控件的按鈕也能夠響應事件.

    實作思路:
        先辦到讓按鈕能夠跟随着手指移動而移動.
        實作按鈕的touchesMoved方法,在touchesMoved方法當中,獲得目前手指所在的點.以前上一個點.
        分别計算X軸的偏移量以及Y軸的偏移量.
        然後修改目前按鈕的transform讓按鈕辦到能夠跟随着手指移動而移動.

        第二步, 實作按鈕的hitTest方法.
        在該方法當中去判斷目前的點在不在按鈕的子控件上.
        如果在按鈕的子控件上.就傳回按鈕的子控件如果不在的話, 就保持系統的預設做法.

    實作代碼:
        第一步,讓按鈕能夠跟随着手指移動而移動
        -(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

            擷取目前的手指
            UITouch *touch = [touches anyObject];
            擷取目前手指所在的點
            CGPoint curP = [touch locationInView:self];
            擷取目前手指的上一個點
            CGPoint preP = [touch previousLocationInView:self];
            計算X軸的偏移量
            CGFloat offsetX = curP.x - preP.x;
            計算Y軸的偏移量
            CGFloat offsetY = curP.y - preP.y; 
            修改按鈕的形變,讓按鈕能夠移動.   
            self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);    
        }

        第二步,實作hitTest方法,判斷手指目前所在的點在不在按鈕的子控件上.

        -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
            把目前所在的點轉換成按鈕子控件上面的點
            CGPoint chatP =  [self convertPoint:point toView:self.chatBtn];
            判斷轉換後的點在不在按鈕的控件上.
            if ([self.chatBtn pointInside:chatP withEvent:event]) {如果在
                直接傳回,也就意味着,目前最适合的View,就是這個按鈕
                return self.chatBtn;
            }else{如果不在,那麼就保持系統原有做法.
              return  [super hitTest:point withEvent:event];
            }
        }