• 事件分发机制Hit-Test View的应用(TabBar凸起不能点击解决方法)


    一、事件分发原理

    --- hitTest:withEvent:方法大致处理流程是这样的:
    首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:
    -- 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil
    -- 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:方法重复前面的步骤,子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图的hitTest:withEvent:方法返回非空对象或者全部子视图遍历完毕:
    -- 若第一次有子视图的hitTest:withEvent:方法返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束
    -- 若所有子视图的hitTest:withEvent:方法都返回nil,则当前视图的hitTest:withEvent:方法返回当前视图自身(self)
    -- 最终,这个触摸事件交给主窗口的hitTest:withEvent:方法返回的视图对象去处理

    • hitTest:withEvent:方法会忽略以下视图:
    1> 隐藏(hidden=YES)的视图
    2> 禁止用户操作(userInteractionEnabled=NO)的视图
    3> alpha<0.01的视图
    4> 如果一个子视图的区域超过父视图的区域(如果父视图的clipsToBounds属性为NO,超过父视图区域的子视图内容也会显示),那么正常情况下在父 视图区域外的触摸操作不会被识别,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。当然,也可以重写pointInside:withEvent:方法来处理这种
    
    综上所述可得:如果父视图的userInteractionEnabled=NO,触摸事件不会继续往下传递给子视图,所以子视图永远无法处理触摸事件。而UIImageView在默认情况下的userInteractionEnabled就是NO。
    

    二、官方详细说明

     

    假设我们现在点击到了图中的E,hit-testing将进行如下步骤的检测(不包含重写hit-test并且返回非默认View的情况)
    1、触摸点在ViewA内,所以检查ViewA的Subview B、C
    2、触摸点不在ViewB内,触摸点在ViewC内部,所以检查ViewC的Subview D、E
    3、触摸点不在ViewD内,触摸点发生在ViewE内部,并且ViewE没有subview,所以ViewE属于ViewA中包含这个点的最小单位,所以ViewE变成了该次触摸事件的hit-TestView
    1、默认的hit-testing顺序是按照UIView中Subviews的逆顺序
    2、如果View的同级别Subview中有重叠的部分,则优先检查顶部的Subview,如果顶部的Subview返回nil, 再检查底部的Subview
    3、Hit-Test也是比较聪明的,检测过程中有这么一点,就是说如果点击没有发生在某View中,那么该事件就不可能发生在View的Subview中,所以检测过程中发现该事件不在ViewB内,也直接就不会检测在不在ViewF内。也就是说,如果你的Subview设置了clipsToBounds=NO,实际显示区域可能超出了superView的frame,你点击超出的部分,是不会处理你的事件的,就是这么任性!
    

     三、事例

    1、解决TabBar凸起部分不能点击

    //重写hitTest方法,去监听发布按钮的点击,目的是为了让凸出的部分点击也有反应
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
        //这一个判断是关键,当页面不显示TabBar,这个按钮还是有反应,这样就不好了
        //self.isHidden == NO 说明当前页面是有tabbar的,那么肯定是在导航控制器的根控制器页面
        //在导航控制器根控制器页面,那么我们就需要判断手指点击的位置是否在发布按钮身上
        //是的话让发布按钮自己处理点击事件,不是的话让系统去处理点击事件就可以了
        if (self.hidden == NO) {
             //将当前tabbar的触摸点转换坐标系,转换到发布按钮的身上,生成一个新的点
            CGPoint newP = [self convertPoint:point toView:self.publicButton];
            
                 //判断如果这个新的点是在发布按钮身上,那么处理点击事件最合适的view就是发布按钮
            if ([self.publicButton pointInside:newP withEvent:event]) {
                return self.publicButton;
            }else{//如果点不在发布按钮身上,直接让系统处理就可以了
                return [super hitTest:point withEvent:event];
            }
        }
     return [super hitTest:point withEvent:event];
    }
    
    将来的自己,会感谢现在不放弃的自己!
  • 相关阅读:
    Hibernate多对一ManytoOne
    eclipse中配置MAVEN并使用阿里云代理
    火车采集器 帝国CMS7.2免登录发布模块
    JavaScript数据类型
    JavaScript 命名规则
    帝国cms 无法生成静态页
    帝国cms 页面统计
    PHP类型转换
    Excel小写金额转大写金额公式
    新时期大站协议脚本邮件群发软件 怎么样
  • 原文地址:https://www.cnblogs.com/TheYouth/p/6560484.html
Copyright © 2020-2023  润新知