OC基础iOS开发iOS精华

iOS-hitTest:withEvent与自定义hit-tes

2015-12-03  本文已影响6847人  cocoa

在做tableView嵌套scrollView的时候怕手势冲突,研究了一下hitTest,虽然最后没用上,但是觉得比较有用,写了一个DEMO,通过重写hitTest:withEvent,实现了超出父视图范围响应触摸事件等自定义hit-testing规则,我的理解还很粗浅,如果有错误或者更优解,欢迎大家指出,我看到后会立即修正~

DEMO

https://github.com/liulishuo/LLSHitTestView

预备知识(M了个JiOS developer library

对于触摸事件的响应,首先要找到能够响应该事件的对象,iOS是用hit-testing 来找到哪个视图被触摸了(hit-test view),也就是以keyWindow为起点,hit-test view为终点,逐级调用hitTest:withEvent。

MJ大神的图

在每个视图类的hitTest:withEvent:打印两次log:1.调用时 2.返回值时


打印log的位置 触摸view2 线索log

hitTest:withEvent:调用顺序:...->base->view2->view3
hitTest:withEvent:返回顺序: view3(nil) -> view2(self) -> base(view2)->...

触摸view1 屏幕快照 2015-12-03 09.52.39.png

hitTest:withEvent:调用顺序:...->base->view2(nil)-> base->view1
hitTest:withEvent:返回顺序: view2(nil)->base, view1(self)->base(view1)->...

hitTest:withEvent:方法的处理流程:

思路

既然系统通过hitTest:withEvent:做传递链取回hit-test view,那么我们可以在其中一环修改传递回的对象,从而改变正常的事件响应链。

实现

if([view isEqual:XXX])
 {
       return nil;
 }

这样做的好处是不会阻断hit-testing检测,既可忽略指定的视图又不会屏蔽其子视图。

    if(_path)
    {
          if(!CGPathContainsPoint(_path.CGPath, NULL, point, NO))
          {
              return nil;
          }
    }

_path 是一段bezier曲线,详见代码。

- (UIView *)getTargetView:(UIView *)view
                    point:(CGPoint)point
                    event:(UIEvent *)event
{
    
    __block UIView *subView;
    
    //逆序 由层级最低 也就是最上层的子视图开始
    [view.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        //point 从view 转到 obj中
        CGPoint hitPoint = [obj convertPoint:point fromView:view];
        //        NSLog(@"%@ - %@",NSStringFromCGPoint(point),NSStringFromCGPoint(hitPoint));
        
        if([obj pointInside:hitPoint withEvent:event])//在当前视图范围内
        {
            if(obj.subviews.count != 0)
            {
                //如果有子视图 递归
                subView = [self getTargetView:obj point:hitPoint event:event];
                
                if(!subView)
                {
                    //如果没找到 提交当前视图
                    subView = obj;
                }
            }
            else
            {
                subView = obj;
            }
            
            *stop = YES;
        }
        else//不在当前视图范围内
        {
            if(obj.subviews.count != 0)
            {
                //如果有子视图 递归
                subView = [self getTargetView:obj point:hitPoint event:event];
            }
        }
        
    }];
    
    return subView;
}
LLSHitTestView ![层级关系](http:https://img.haomeiwen.com/i226702/c46b85b96314e86d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

我们在层级比较高的view1使用自定义的hit-testing规则,其上的2、3、4,无论是否超出边界,均能正常响应点击事件,详见代码。

问题

上一篇下一篇

猜你喜欢

热点阅读