iOS中事件链和响应链的传递方式

974 阅读2分钟

项目中遇到了这个问题(collection添加手势后不响应点击事件),虽然已经有了两种解决方案,但是有点不满足,之前对事件链早有耳闻,想想有没有办法通过这种方式来解决。

ps:写这篇文章的时候还未解决问题,目的是为了把学到的知识先整理起来,要不然问题解决之后把原理忘了就得不偿失

参考文章: 史上最详细的iOS之事件的传递和响应机制-原理篇,一篇很好的参考资料

事件的传递:通过hit-test方式传递给子视图 手指触碰屏幕,由UIApplication->window->处理事件最适合的view 证明:

[self.view addSubview:cyan];
[cyan addSubview:puple];
[puple addSubview:btn];

//每个view都重写hitTest方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    NSLog(@"%s\t%d", __func__, __LINE__);
    
    UIView *view = [super hitTest:point withEvent:event];
    
    NSLog(@"last     %s\t%d", __func__, __LINE__);
    
    return view;
}

点击橙色按钮

打印结果

-[cyanView hitTest:withEvent:]	16
-[purpleView hitTest:withEvent:]	15
-[ShapeButton hitTest:withEvent:]	30
last     -[ShapeButton hitTest:withEvent:]	36
last     -[purpleView hitTest:withEvent:]	19
last     -[cyanView hitTest:withEvent:]	20
-[cyanView hitTest:withEvent:]	16
-[purpleView hitTest:withEvent:]	15
-[ShapeButton hitTest:withEvent:]	30
last     -[ShapeButton hitTest:withEvent:]	36
last     -[purpleView hitTest:withEvent:]	19
last     -[cyanView hitTest:withEvent:]	20
点击事件

结论:

从父视图传递到子视图,当父视图不能接受触摸事件(例如:alpha<0.01...)时,子视图也不能接受点击事件
hit-test方法会不断调用,子view的hit-test方法,当子view有返回值之后才会返回父view的hit-test返回值

有些人以为hit-test是事件链,官方文档也有这样的解释

Returns the farthest descendant of the receiver in the view hierarchy (including itself) that contains a specified point.
返回包含指定点的视图层次结构(包括自身)中接收器的最远的后代。

我认为,hit-test不光包含事件链还包括响应链, hit-test方法本身会调用子类的hit-test方法,由父view向子view传递hit-test方法,这叫做事件链. 在hit-test方法中,如果有view响应就继续向子view的hit-test传递,如果没有则返回nil,最后返回的view就是需要做出响应的那个view,然后父view返回自身。这是一个循环

这个也就对应了上面hit-test方法为什么会走两次.