美文网首页iOS学习iOS 开发每天分享优质文章iOS 开发成长中心
通过拦截 hitTest:withEvent 方法,解决超出父视

通过拦截 hitTest:withEvent 方法,解决超出父视

作者: i_have_an_Apple | 来源:发表于2016-12-14 21:46 被阅读150次

由于项目需求,我需要在一个高度为50的控件上面创建一个下拉菜单,效果如下


screenshot.png

当我做完之后发现,下拉菜单的下拉选择项不能点击


screenshot.png
这是因为我们的控件高度只有50,但是下拉菜单的高度超出了控件的大小,这样,我们就接受不到点击事件了
这边找了一个比较详细的图,来描述事件的分发
226702-dd53b5a6df2f3ea5.png

每个 view 都会有

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    UIView *view = [super hitTest:point withEvent:event];

    return view;
}

这样一个方法
这个方法会判断当前点击的“点”是否在本 view 上,如果在本 view 上,就继续寻找本 view 的 Subview,还是通过此方法判断点击的“点”是否在 Subview 上,直到找完所有的 Subview,然后这个方法就会 return 这个最终的 Subview 并一层层的向上传递给 UIWindow,这样我们就拿到了屏幕上面最终响应的 view。

回到我们最开始遇到的问题。

由于我们下拉菜单超出了我们的自定义控件,当我们点击到下拉菜单时,从 UIWindow 开始通过 hitTest 方法向下寻找响应的 view,当查找到我们的自定义控件时,就会 return 了,因为我们点击的“点”已经超出了自定义控件,也就是说,这个“点”不在我们的自定义控件上,所以在自定义控件上面的下拉菜单无论如何也不会响应。

所以,我们只要手动的去 return 我们的下拉菜单,手动的去连接起这个 响应 view 的链,我们的下拉菜单就能响应

- (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;
}

这个方法的目的就是找到点击的“点”最终所在的 subview,然后 return。

我们再回到我们的响应链断掉的地方,也就是自定义控件内的 hitTest 方法

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    UIView *view = [super hitTest:point withEvent:event];
  
    //由于响应链在此处断开,我们就去手动寻找最终响应的子视图,传入本 view 遍历本 view 的子视图
    UIView *tempview = [self getTargetView:self point:point event:event];
    if (tempview) {
        view = tempview;
    }
    
    return view;
}

手动找到点击的点所在的 subview,并在断开的地方 return,这样我们的下拉菜单就能响应点击了

相关文章

网友评论

    本文标题:通过拦截 hitTest:withEvent 方法,解决超出父视

    本文链接:https://www.haomeiwen.com/subject/dxhwmttx.html