iOS-响应链相关

iOS开发---图解事件的产生和响应

2019-08-18  本文已影响0人  祀梦_

iOS开发---图解事件的产生和响应

本博客会从事件的产生和响应这两个方面来完整的解释事件。

我们先看一下本文的主要内容,正文我会一一讲解:

产生:事件如何从父控件传递到子控件并寻找到最合适的view

响应:找到最合适的view后事件的处理(touches方法的重写,也就是事件的响应)

事件的产生

什么是事件(UIEvent)?

image

事件的产生

  1. 当用户的手指触摸屏幕的某一个view的时候,此时就发生了触摸事件,产生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,为什么是队列而不是栈?因为队列的特点是FIFO,即先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。
  2. UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)
  3. 然后由主窗口决定如何将事件交给最合适的响应者(UIResponder)来处理

事件的传递

如何寻找最合适的UIView

UIView接收触摸事件的条件
  1. userInteractionEnabled属性为YES,该属性表示允许控件同用户交互。
  2. Hidden属性为NO。控件都看不见,自然不存在触摸
  3. opacity属性值0 ~0.01。
  4. 触摸点在这个UIView的范围内。
事件传递的流程
  1. 触摸事件发生后,UIApplication会先发送事件给应用程序的主窗口(keyWindow),判断主窗口(keyWindow)自己是否能接受触摸事件
  2. 触摸点是否在自己身上
  3. 在子控件数组中从后往前遍历子控件,重复前面的两个步骤(首先查找数组中最后一个元素)
  4. 如果没有符合条件的子控件,那么就认为自己最合适处理
image
事件传递的底层实现

前面我们为大家清楚的介绍了事件传递的流程,但是大家肯定会有两个疑惑:

一、你怎么知道自己能否接收触摸事件

二、你怎么知道触摸点在不在自己身上

hitTest:withEvent:方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.判断自己能否接收触摸事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    // 2.判断触摸点在不在自己范围内
    if (![self pointInside:point withEvent:event]) return nil;
    // 3.从后往前遍历自己的子控件,看是否有子控件更适合响应此事件
    int count = self.subviews.count;
    for (int i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[i];
        CGPoint childPoint = [self convertPoint:point toView:childView];
        UIView *fitView = [childView hitTest:childPoint withEvent:event];
        if (fitView) {
            return fitView;
        }
    }
    // 没有找到比自己更合适的view
    return self;
}
pointInside:withEvent:方法

事件的响应

找到最合适的视图控件后,就会调用控件的touches方法来做具体的事件处理,这就是事件的响应。

UITouch对象

响应者对象

事件的响应流程

  1. 当我们找到最合适的View后,如果当前的View有添加手势,那么直接响应相应的事件,如果没有手势事件,那么会看其是否实现了如下的方法:
- (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;://触摸终端
  1. 如果有实现那么就由此View响应,如果没有实现(即不能处理当前事件),那么事件将会沿着响应者链(Responder Chain)进行传递,直到遇到能处理该事件的响应者(Responsder Object)

响应者链

image

事件的整个流程总结

  1. 当一个事件发生后,事件会从父控件传给子控件,也就是说由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的传递,也就是寻找最合适的view的过程
  2. 接下来是事件的响应。首先看initial view能否处理这个事件,如果不能则会将事件传递给其上级视图(inital view的superView);如果上级视图仍然无法处理则会继续往上传递;一直传递到视图控制器view controller,首先判断视图控制器的根视图view是否能处理此事件;如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传递;(对于第二个图视图控制器本身还在另一个视图控制器中,则继续交给父视图控制器的根视图,如果根视图不能处理则交给父视图控制器处理);一直到window,如果window还是不能处理此事件则继续交给application处理,如果最后application还是不能处理此事件则将其丢弃
  3. 事件的响应中,如果某个控件实现了touches方法,则这个事件将由该控件来接受,如果调用了[super touches….];就会将事件顺着响应者链条往上传递,传递给上一个响应者;接着就会调用上一个响应者的touches方法

hitTest:和pointInside:的应用

屏蔽

穿透

上一篇下一篇

猜你喜欢

热点阅读