IOS面试大全

iOS中事件传递和事件响应

2018-07-19  本文已影响40人  zdl

1.响应者对象

先介绍一下响应者对象,在iOS中并不是所有的对象都能够处理事件,只有继承UIResponder的对象才能够接收和处理事件,我们称为响应者对象。 UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都能接收并处理事件。

2.iOS中的事件

iOS中的事件可以分为三大类型:

触摸事件 、传感器事件 、远程控制事件

本文主要讨论触摸事件

3.事件传递和事件响应的区别

首先我们先来了解一下事件传递和事件响应的区别,以便更好的理解后面的内容 。(千万不要混淆这两者的概念)
事件的传递是从上到下的顺序 (UIApplication-->UIWindow-->递归找到最合适处理的View),即从父控件到子控件的寻找过程
事件响应是从下到上的顺序 (实现View的touchesBegan方法,如果该View不能处理事件,则向上传递给它的父控件),从子控件传递给父控件

4.iOS中事件的产生和传递

  1. 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,而不是放入栈中保存,因为队列的特点是FIFO(先进先出),先产生的事件先处理才符合常理

  2. UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常先发送事件给应用程序的主窗口(keyWindow)

  3. 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理

事件传递最重要的就是找到合适的View来接收事件, 应用是如何找到最合适的View来接收处理事件的呢?

1.首先判断主窗口(keyWindow)自己是否能接受触摸事件

2.判断触摸点是否在自己身上

3.子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)

4.view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。

5.如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。

有两个重要的方法 帮助应用寻找到最合适的View

hitTest:withEvent:方法 (自定义View 实现该方法)

- (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] == NO) return nil; 
    // 3.从后往前遍历子控件数组 
    int count = (int)self.subviews.count; 
    for (int i = count - 1; i >= 0; i--)     { 
    // 获取子控件
    UIView *childView = self.subviews[i]; 
    // 坐标系的转换,把窗口上的点转换为子控件上的点 
    // 把自己控件上的点转换成子控件上的点 
    CGPoint childP = [self convertPoint:point toView:childView]; 
    UIView *fitView = [childView hitTest:childP withEvent:event]; 
    if (fitView) {
    // 如果能找到最合适的view 
    return fitView; 
    }
    } 
    // 4.没有找到更合适的view,也就是没有比自己更合适的view 
    return self;
    }

pointInside:withEvent:方法

//判断当前点是否在View上
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    
    //判断点在不在view区域
    if (CGRectContainsPoint(self.btn.bounds, point)) {
        return YES;
    }
    return NO;
    
}

事件的响应

找到合适的View之后就会调用该View的touches方法进行响应处理具体的事件,找不到最合适的View,就不会调用touches方法进行处理

touchesBegan方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    NSLog(@"%s",__func__);
    }

一般默认做法是控件将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理 (即调用super的touches方法)。

那么如何判断响应者的上一个响应者是谁呢?有以下两个规则

  1. 判断当前是否是控制器的View,如果是控制器的View,那么上一个响应者就是控制器
  2. 如果不是控制器的View,那么上一个响应者就是其父控件
如果控制器也不响应touches方法,就把事件交给UIWindow,如果UIWindow也不响应,交把事件给UIApplication,如果都不响应事件那么事件就会抛弃作废。
最后总结来说一次完整的触摸事件的传递响应过程为:

UIApplication-->UIWindow-->递归找到最合适处理的控件-->控件调用touches方法-->判断是否实现touches方法-->没有实现默认会将事件传递给上一个响应者-->找到上一个响应者-->找不到方法作废

注:以下三种情况不能接收触摸事件

  1. 不允许交互: 即空间的userInteractionEnabled = NO;
  2. 隐藏:隐藏的控件不能接收触摸事件
  3. 透明度:如果一个控件的透明度设置 <0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明
在这里就不举具体的栗子,以上有不正确的地方,欢迎大神指正。
上一篇下一篇

猜你喜欢

热点阅读