面试被问过两次了的一个问题,记录一下:假如一个 UIView(我们称作 FatherView),有一个 subview(我们称作 ChildView),这个 ChildView 在 FatherView 的 frame 外部,那么默认情况下,这个 ChildView 被点击的时候,并不会触发 FatherView 的点击响应链(这是合情合理的,FatherView 所在的位置都没被点击,它不遍历 subviews,不作回应是正常的),这时候怎样才能让这个 ChildView 相应点击事件?
其实思路是很简单的,监控屏幕上的所有点击事件的区域,如果这个区域和我们的 UIView 以及其 subview 的 CGRect 有重合,就作出反应。
也很容易查到 UIKit 提供了一个现成的方法来做到这件事:
Hit Testing in a View Screen Shot
代码是很简单的,现在让灰色的视图作为父视图,蓝色、红色的作为子视图,只要灰色视图实现了上述方法,就可以让蓝色视图像红色视图一样,可以相应点击事件。
Code Snippet Code Snippet至此都是很简单的内容,需要注意的如下:
- 你会发现 FatherView 里面的这个 <code>HitTest</code> 会被频繁执行,你随便点击一个空白处,这个方法都会执行,这必然带来性能消耗,这也是 Apple 默认不使用这个方式的原因之一吧
- <code>HitTest</code> 会使得我们的蓝色的 UIView 可以相应点击,但是它被点击的之后,执行的方法我们应该按常规的方式写(比如给它加一个 <code>UIGestureRecognizer</code> ,然后让这个 Gesture 有一个对应的 selector)。如果把需要执行的方法写在了 <code>HitTest</code> 里面,写在 <code>return result</code> 之前的话,我发现这些方法会多执行一次(在我当前写的这个简单 demo 里,我发现点击任意地方,<code>HitTest</code> 方法都会连续执行两次)
网友评论
class HitView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
for view in self.subviews {
if CGRectContainsPoint(view.frame, point) {
return view
}
}
return super.hitTest(point, withEvent: event)
}
}
```
CGPoint pointForTargetView = [self.targetView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.targetView.bounds, pointForTargetView)) {
return [self.targetView hitTest:pointForTargetView withEvent:event];
}
return [super hitTest:point withEvent:event];
```
应该把点击位置转换到目标视图的坐标系中再做包含判断。