这几天遇到一个问题,就是有个效果要穿透tableview(背景色是clear,设置了inset,所以可以看到下面的其他视图),这个时候虽然可以看到背后的视图,但是无法点击,思考了很长时间,最后想到了通过响应者链条来实现这一效果.
简单的说一下响应者链条,拿一个简单的点击事件来说,为什么当你点击一个button它会有响应事件,这个就是响应者链条来确定的它就是响应对象,大家知道所有的视图和控制器都是响应者对象,因为他们都继承自UIResponder这个类,每当有一个点击事件,它会从最下面(window)的视图或者控制器开始查询,会遍历它的子视图,按添加的先后顺序倒序开始查询,为什么是倒序呢?因为你最后一个添加的视图是在最上面,一般情况下,你点击的视图是你看到的视图,也就是放在最上面的视图,所以倒序开始排查,若果发现某一个子视图包含了这个时间(可以理解为点击的点在这个子视图上),系统会继续遍历子视图的子视图,继续按上面的方法开始排查,直到某一个视图它没有子视图,或者点击的点没有落在他的子视图的范围内,这个时候,就把自己作为最终的响应者(第一响应者)返回,这个就是大体的响应者链条的原理,详细的原理理解大家可以上网搜索"响应者链条",会有很多解释,这里就不太多的解释了.
今天主要是用到了,响应者链条中的一个重要的方法- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ,这个方法是响应者链条能够实现的底层依托,每一个继承自uiresponder的类都会有这个方法,源码的实现如下:
```
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
}
return self;
}
```
大家可以看着这个代码理解响应者链条,而解决我们今天的问题,只需要修改一个地方就可以了,把最后的return self 改成 return nil ,就可以了,虽然简单,但是需要理解为什么这样改还有在哪一个view里面改.
首先,我们来确定是修改哪一个view的这个方法,因为我们想要tableView可以被穿透,所以我们定位到tableView上面,来重写UITableView的- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法(分类实现).
其次,需求:"只是inset部分由于tableView的颜色是透明的,可以看到后面的按钮,需要点击按钮的时候有响应事件,但是正常的tableViewCell,并不会有穿透效果",所以如果tableView的子视图(包括cell,一级其他的子视图)有是第一响应者的,还是需要正常的返回,但是如果最后是tableView本身返回,说明你点击的是inset部分,这个时候我们需要按钮来响应时间,所以当tableView本身被返回的时候,我们返回nil,这个时候他的上一级接收到nil后,会继续执行其他的子视图,而tableView这个子视图就被跳过了,从而实现了tableView的穿透效果.
好了,到这里问题被很好的解决,可以开心的回家吃饭了.