响应链传递机制

iOS中的事件的产生和传递

1. 事件的产生
  • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,为什么是队列而不是栈?因为队列的特点是FIFO,即先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。
  • UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
  • 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
  • 找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。
2. 事件的传递
  • 触摸事件的传递是从父控件传递到子控件
  • 也就是UIApplication->window->寻找处理事件最合适的view

注 意: 如果父控件不能接受触摸事件,那么子控件就不可能接收到触摸事件

3. 应用如何找到最合适的控件来处理事件?
  • 首先判断主窗口(keyWindow)自己是否能接受触摸事件
  • 判断触摸点是否在自己身上
  • 子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)
  • 比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
  • 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。

UIView不能接收触摸事件的三种情况:

  • 不允许交互:userInteractionEnabled = NO
  • 隐藏:如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接受事件
  • 透明度:如果设置一个控件的透明度<0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明。

注 意:默认UIImageView不能接受触摸事件,因为不允许交互,即userInteractionEnabled = NO。所以如果希望UIImageView可以交互,需要设置UIImageView的userInteractionEnabled = YES。


- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    // 1.判断下窗口能否接收事件
    if (self.userInteractionEnabled == NO || self.isHidden == 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) {
            return fitView;
        }
    
    }
    // 4.没有找到更合适的view,也就是没有比自己更合适的view
    return self;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容