iOS开发实战iOS学习开发iOS学习笔记

一个99%刚入职场的iOSer踩过的坑

2018-12-25  本文已影响105人  溪石iOS

小胡刚入职的第二天,iOS组长给他一个实习任务:用UICollectionView搭一个商品选择界面。小胡觉着这不难啊,估摸着半小时就能搭完页面,调试调试,对接下接口,半天也完成了。
故事总是这样,从喜剧开始,最终变成了一部悬疑剧:
大半天过去了,小胡惴惴不安地找到组长:“我遇到一个诡异的问题...”
组长:“什么问题?”
“UICollectionView的didSelectItemAtIndexPath怎么点都不响应!”
“哦?我来看看”,说着,组长打开“视图层次查看器”,结果发现,UICollectionView 被加到一个全屏的UIImageView背景上,而UIImageView的userInteractionEnabled默认为NO !!!
“这样你当然什么触摸事件都接收不到了呀!”
小胡羞的满脸通红,感觉这个失误和没插电源,却说电脑坏了一样愚蠢,不过他还是说出了心中的疑问:组长,我明明点击的是UICollectionView,为什么触摸事件却被父视图给屏蔽了呢?
组长看着小胡,“看来,得给你理一理响应者链了”:


响应者链有两个过程

响应者链是iOS处理“事件”的一种机制,我们可以简单理解为触摸事件在类中传递的过程,而构成iOS界面上的几乎所有类,UIApplication、UIView、UIViewController 都是直接继承自UIResponder,所以当用户点击屏幕后,由哪个UIResponder来处理这个点击,就需要进行一番选择。
一个屏幕上,UIResponder 被组织成了一棵树:

UIResponder结构树
触摸事件由根结点逐层往下传递,这是第一个传递过程,叫做命中测试,是由hitTest 方法完成的:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

目的是为了找到点击发生在哪个UIResponder的范围内,即所谓的“命中者”。

覆盖 hitTest 时,要调用 [super hitTest:point withEvent:event],否则链条被中断,子视图将不会执行hitTest,也就无法参与到响应者链中来。

找到命中者后,就要在这些命中者中找个“响应者”,响应者是有“应聘条件”的:

这个过程是与第一个过程相反的,由子视图向上传递:

寻找响应者的过程

第一个满足以上条件的命中者,如果实现

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

就被确认为响应者,系统认为事件被正确的处理了,事件传递过程到此结束
命中者中找不到响应者,怎么办?事件就会被一直传递,直到遍历了UIResponder 树上全部结点,最后由UIWindow做最后处理。

响应者链作用

  1. 可以让一个触摸事件发生的时候让多个响应者(在同一条响应链上)同时响应该事件: 通过调用[super touchesXXX]
  2. 在需要控制全局的用户点击事件的时候,可以通过 UIApplication 的beginIgnoringInteractionEventsendIgnoringInteractionEvnets来关闭或者开启事件传递。
  3. 可以重写
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event

来扩大UIButton的响应范围(当然缩小也可以)。

  1. 当目标视图点击区域不在父视图的Frame中时,也能响应事件, 比如咸鱼的tabbar


    凸起效果的Tabbar

这里给一个处理的示例代码:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (!self.clipsToBounds && !self.hidden && self.alpha > 0) {
        for (UIView *subview in self.subviews.reverseObjectEnumerator) {
            CGPoint subPoint = [subview convertPoint:point fromView:self];
            UIView *result = [subview hitTest:subPoint withEvent:event];
            if (result != nil) {
                return result;
            }
        }
    }
    return nil;
}

小结

响应者链是点击事件命中和分配给响应者的过程,它把触摸命中者组成一个链条,让开发人员有机会介入事件的响应过程,从而更灵活的处理点击等事件。

思考题

如何编写hitTest,使得当前视图不响应点击,又不影响子视图响应事件呢?期待你的留言,只有动手写过的代码,才是自己的!

上一篇 下一篇

猜你喜欢

热点阅读