事件处理

作者: 01_Jack | 来源:发表于2015-07-26 07:50 被阅读2493次

这篇文章主要讲以下两个方法,如果你对响应者链条还不够熟悉,请接着往下看,如果足够熟悉请跳到正文。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ;

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
响应者链条

在这里演示一个最简单的按钮响应

响应者链条示意图.png

在主窗口控制器的view上添加一个按钮,当用户点击按钮时,系统究竟做了哪些事情?首先我们要知道UIResponder,只有继承自UIResponder的对象才可以处理事件。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

这几个很常用的方法都是从UIResponder继承过来的。

回到响应者链条。当用户点击按钮,系统最先响应这个点击事件,由AppDelegate接收,然后不断上抛直到到达UIButton,如图中实线所示。当UIButton接收到这个事件,UIResponder相应的方法开始工作,系统默认做法是调用控件对应的super方法,值得一提的是:super会调用父类中的方法,而此处刚好会调到当前控件父控件所对应的方法。此时的事件传递方向刚好相反,从UIButton向底层抛,直到系统接收并做出响应,如图中虚线所示。这一来一回,我们称之为响应者链条

UIView中的方法

首先来回顾一下继承关系:UIWindow\UIButton -> UIView -> UIResponder -> NSObject

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ;

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

这两个不太常见的方法其实是属于UIView的。

上文中响应者链条提到过,系统接收用户点击事件后会将事件不断上抛,直到到达UIButton,那么这中间又是那些东西在运作?

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

这个方法很好理解,event事件在point点是否响应。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ;

下面重点说说这个方法究竟做了些什么。

  • 文字
1.判断当前控件userInteractionEnabled、hidden、alpha这三个属性的值
2.调用 pointInside: withEvent: 方法
3.从后向前遍历子控件,并调用子控件的 hitTest: withEvent: 和 pointInside: withEvent: 方法
  • 代码
 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    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 - 1;
    for (int i = count; i >= 0 ; i--) {
        UIView *view = self.subviews[i];
        CGPoint p = [view convertPoint:point fromView:self];
        if ([view pointInside:p withEvent:event]) {
            return [view hitTest:p withEvent:event];
            break;
        }
    }
    
    return self;
}

先判断当前控件能否接收事件,如果不能返回空,如果能再判断当前点能否响应事件,如果不能返回空,如果能从后向前遍历子控件,判断当前子控件当前点能否响应事件,如果不能继续判断下一个子控件直到子控件遍历完成(如果所有子控件都不满足条件,那么当前控件就是响应这个事件最合适的控件),如果能那么返回当前子控件的hitTest方法并跳出循环,进入子控件hitTest重复当前操作。

再解释一下为什么要从后向前遍历子控件。这个问题可以从用户的角度思考,通常情况用户点击app某个位置,最好是展现在用户最上层的控件做出响应,而非底层(不排除某些特殊需求)。所以苹果选择倒序遍历,而不是顺序。

  • 总结
 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ;

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

通过上文分析,不难看出这两个方法可以实现某些特殊需求,如局部拦截控件的响应事件,实现子控件在父控件之外仍可响应等等。

相关文章

  • JS 事件

    目录 事件流 事件处理程序HTML事件处理程序DOM0级事件处理程序DOM2级事件处理程序IE事件处理程序跨浏览器...

  • react事件处理

    一,事件处理 写法:on+事件名称= {事件处理函数} 类组件触发写法on+事件名称 = 事件处理函数 ---...

  • 跨浏览器的事件处理程序

    事件处理程序有DOM0级事件处理程序、DOM2级事件处理程序,IE事件处理程序,DOM0级事件处理程序具有简单,跨...

  • App事件中心

    App事件中心,事件的的生产端和处理端分离,事件处理结果广播通知,事件状态(初始化、处理中和处理完成)管理,事件类...

  • Chapter 07. Broadcast

    阅读原文 7.1 . 理论概述 广播事件处理属于系统级的事件处理(一般事件处理是属于View级的事件处理) 一个应...

  • iOS和Flutter里的事件处理

    目录先说一下事件处理里的被处理者:事件一、iOS里的事件二、Flutter里的事件然后说一下事件处理里的处理者:响...

  • react文档——事件处理

    事件处理 React 元素的事件处理和 DOM 元素的事件处理非常相似。但也有一些语法差异: React 事件使用...

  • DOM事件的问题!

    1.事件冒泡 2.事件捕获 事件处理程序 1.HTML事件处理程序 2.DOM 0级事件处理程序 3.DOM 2级...

  • 2021-09-22 GUI(事件监听机制)

    事件监听机制组成事件源(组件)事件(Event)监听器(Listener)事件处理(引发事件后处理方式) 事件监听...

  • attachEvent和addEventListener区别

    attachEvent是IE的事件处理方法,是DOM0事件处理程序,只能在事件冒泡阶段触发。接收两个参数,事件处理...

网友评论

  • d86a5242a9a7:遍历会找到最终的响应者(如果所有的接收者都能交互),即用户点击的那个控件作出反应,所有不管是从前往后遍历,还是从后往前遍历,如果底层处理方法的逻辑不变,这两种遍历都会找到并且返回同样的结果,所以从后往前遍历和用户没啥关系,要说有的话,效率估计会高点,因为如果界面很多视图都叠加在一起,用户点击的话,那么最外层的view视图产生touchesbegan的几率会很大,所以从这一点来讲,从后往前估计就是为了效率,真正具体的原因不晓得,没学过汇编,分析不出这个过程一个 逻辑的数据结构
    d86a5242a9a7:@d86a5242a9a7 最外层的view视图产生touches对象
  • _____柠檬:我尝试了一下。
    v1 add v2 add v3 视图;
    打印了这两个方法,hitTest pointinside 为什么他们分别都调用了2次?
  • 终生程序员小松哥:大体正确,除了一下两点:
    1,响应链仅仅指的是虚线部分;
    2,实线部分也有误:UIviewcontrol是到不了的;
    详细见Apple的开发者文档
    01_Jack:@松果果 最近比较忙,等等一定拜访
    终生程序员小松哥:@01_Jack 也来我博客看看,帮我也把关
    01_Jack:@松果果 感谢指出错误,有时间修改😁
  • 01_Jack:@Fengxinliju 已回复,可以的
  • 01_Jack:@Fengxinliju 利用这两个方法实现方式有很多,最暴力的方式自定义JKView,在类中实现hittest并返回self,当然这样做有很多弊端,可以写的更好,不过现在电脑没网,手机回复不方便
  • 01_Jack:@Fengxinliju 这只是一种逆向思考,可以实现类似功能,并不是真的是苹果的实现方式,闭源嘛
    FengxinLi:@01_Jack 想让一个UIbutton或者uiimageview他的父类响应事件的话 这个可以办到不?用这个响应链。
  • FengxinLi: 还有一问- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 里面的代码是怎么知道是这样执行的?
  • FengxinLi:请问楼主 如果这样。一个UIView上面加Button。Button加一个事件但是不响应。我想让uivew响应这个事件。这个可以办到吗?

本文标题:事件处理

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