假设您有一个superview,它的大小比它的子视图要小。将superview的clipsToBound属性设置为false。如果您点击超出superview界限的子视图的突出区域,为什么命中测试返回零?
我的理解是,命中测试从子视图开始,一直工作到superview。那么,为什么要测试的superview的属性比子视图还要晚呢?或者,命中测试是否从根开始到树视图,如视图控制器->、主视图->子视图?
我从here中找到了一个定制的点击测试,它允许你点击超级视图界限之外的子视图区域,但我不知道为什么要改变子视图的顺序(它有效,我只是不知道为什么)。我的例子甚至只有一个子视图。
class ViewController: UIViewController {
let superview = CustomSuperview(frame: CGRect(origin: .zero, size: .init(width: 100, height: 100)))
let subview = UIView(frame: CGRect(origin: .zero, size: .init(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.superview)
self.superview.backgroundColor = .red
self.superview.clipsToBounds = false
self.superview.addSubview(self.subview)
self.subview.backgroundColor = .blue
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
self.subview.addGestureRecognizer(tap)
}
@objc func tapped(_ sender: UIGestureRecognizer) {
print("tapped")
}
}
class CustomSuperview: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if clipsToBounds || isHidden || alpha == 0 {
return nil
}
for subview in subviews.reversed() {
let subPoint = subview.convert(point, from: self)
if let result = subview.hitTest(subPoint, with: event) {
return result
}
}
return nil
}
}发布于 2020-12-05 21:58:46
,我的理解是,命中测试从子视图开始,一直工作到超级视图。
那么你的理解是完全错误的。我们来解决这个问题。首先,让我们澄清其他一些误解:
reversed是一种完全的红鲱鱼,与之无关。子视图总是按反向顺序进行测试,因为前面的子视图需要优先于后面的子视图。clipsToBounds也是一种完全红色的鲱鱼。它所做的就是更改是否可以在其superview之外看到子视图;它对您是否可以触摸其superview之外的子视图没有任何影响。好吧,那这是怎么回事?让我们以包含子视图A的视图V为例,假设A在V之外。假设你可以看到A,然后点击A。
现在点击测试开始了。但事情是这样的:它从窗口的级别开始,然后沿着视图层次结构工作。因此,窗口从询问其子视图开始;只有一个视图控制器的主视图。
因此,现在我们进行递归,视图控制器的主视图询问它的子视图。其中之一是V。“嘿,V,你体内有水龙头吗?”“不!”(你必须同意,这是正确的答案,因为我们已经说过A在V.之外)
所以视图控制器的主视图放弃了V,从来没有发现点击是在A上,因为我们从来没有递归过那么远。所以它向上报告:“水龙头没有出现在我的任何子视图上,所以我必须报告水龙头是在我身上的。”水龙头已经通到视图控制器的主视图。
但是您可以通过重写hitTest的实现来改变这种行为。
override func hitTest(_ point: CGPoint, with e: UIEvent?) -> UIView? {
if let result = super.hitTest(point, with:e) {
return result
}
for sub in self.subviews.reversed() {
let pt = self.convert(point, to:sub)
if let result = sub.hitTest(pt, with:e) {
return result
}
}
return nil
}https://stackoverflow.com/questions/65161861
复制相似问题