收藏iosiOS

iOS 点击穿透上层视图响应事件

2018-02-10  本文已影响1536人  iOS_肖晨

昨天被问到了一个问题,如下

一个子视图能不能透过上层视图来响应自己的点击事件?如果能,怎么实现?

这种题用脚想都是能啊,可怎么做???
经过简单研究,发现了解决办法。如下:

这里涉及到一个视图的方法
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
这是在UIView(UIViewGeometry)视图结构分类里面的方法,使用方法如下:

1. 背景知识

iOS系统检测到手指触摸(Touch)操作时会将其放入当前活动Application的事件队列,Application会从事件队列中取出触摸事件并传递给key window(当前接收用户事件的窗口)处理,window对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,称之为hit-test view。

window对象会在首先在view hierarchy的顶级view上调用hitTest:withEvent:,此方法会在视图层级结构中的每个视图上调用pointInside:withEvent:,如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是hit-test view。

2. 处理流程

  1. 首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
  2. 若返回NO,则hitTest:withEvent:返回nil;
  3. 若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
  4. 若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
  5. 如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。

3. 使用

直接参考代码

// 在父视图中实现方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // 1.这是当前点击的视图,如果没有找到合适的响应操作的视图,则直接返回这个
    UIView *view = [super hitTest:point withEvent:event];

    // 2.将父视图坐标转成我想要响应事件的视图的坐标
    CGPoint buttonPoint = [self convertPoint:point toView:self.button];

    // 3.判断该坐标是否在视图内部,如果是,则返回该视图
    if ([self.button pointInside:buttonPoint withEvent:event]) {
        return self.button;
    }
    return view;
}

代码写起来也是简单明了,但有几个注意事项,如下:

  1. 设置hidden = YES;仍然是可以通过该方法响应事件。
  2. 设置alpha = 0.001;也是可以通过该方法响应事件。
  3. 子视图的 origin 即使超出父视图的范围也是可以响应事件。
  4. 设置userInteractionEnabled = NO;则不可以响应事件了。

这种方式可以做出很多风骚的操作,就看各位的想象力了~

上一篇下一篇

猜你喜欢

热点阅读