我的应用程序遇到了挑战。我希望能够点击一个子视图并确定点击了哪个子视图的图层。我(天真?)选择使用子视图的根层的hitTest(:CGPoint)方法。结果不是我预期的。我创建了一个游乐场来说明困难。
游乐场(以下代码):
这是它的样子:
在操场上运行时,我开始点击,从白线的顶部开始向下移动。这是轻击手势处理方法产生的打印语句:
点击根视图@(188.5,12.5)
命中测试返回带有帧(0.0、0.0、375.0、668.0)的“根视图->根层”点击根视图@(188.5,49.0)
命中测试返回带有帧(0.0、0.0、375.0、668.0)的“根视图->根层”点击根视图@(188.5,88.5)
命中测试返回带有帧(0.0、0.0、375.0、668.0)的“根视图->根层”点击子视图@(140.0,11.0)
点击子视图@(138.5,32.5)
点击子视图@(138.5,55.5)
点击子视图@(138.0,84.0)
点击子视图@(139.0,113.0)
命中测试返回带有帧(50.0、100.0、275.0、468.0)的“子视图->根层”点击子视图@(138.0,138.5)
命中测试返回带有帧(50.0、100.0、275.0、468.0)的“子视图->根层”点击子视图@(140.0,171.5)
命中测试返回带有帧(50.0、100.0、275.0、468.0)的“子视图->根层”点击子视图@(137.5,206.5)
命中测试返回带有帧(50.0、100.0、175.0、268.0)的“子视图->子层”点击子视图@(135.5,224.0)
命中测试返回带有帧(50.0、100.0、175.0、268.0)的“子视图->子层”
您可以看到,当我在根视图中点击时,情况是“正常的”。进入子视图后,点击测试将不再找到图层,直到我在子视图顶部以下100多个点为止;这时会遇到子视图的根层。遇到子视图的根层的子层后的100分。
这是怎么回事?好吧,显然,当将子视图添加到根视图时,子视图的根层成为根视图的根层的子层,并且其框架已更改以反映其在根视图的根层而不是子视图中的位置。 / p>
所以,可能有以下问题:
谢谢您抽出宝贵的时间来考虑我的问题。
这是游乐场代码:
//: [Previous](@previous)
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
class View : UIView {
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(2.0)
context.move(to: CGPoint(x: rect.midX, y: 0))
context.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
context.strokePath()
}
}
private var subview: View!
override func loadView() {
let rootView = View(frame: CGRect.zero)
rootView.backgroundColor = .black
rootView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))))
rootView.layer.name = "Root View -> Root Layer"
self.view = rootView
}
override func viewDidLayoutSubviews() {
subview = View(frame: view.frame.insetBy(dx: 50.0, dy: 100.0))
subview.backgroundColor = .red
subview.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))))
subview.layer.name = "Sub View -> Root Layer"
let sublayer = CALayer()
sublayer.bounds = subview.layer.bounds.insetBy(dx: 50, dy: 100)
sublayer.position = CGPoint(x: subview.layer.bounds.midX, y: subview.layer.bounds.midY)
sublayer.backgroundColor = UIColor.gray.cgColor.copy(alpha: 0.5)
sublayer.name = "Sub View -> Sub Layer"
subview.layer.addSublayer(sublayer)
view.addSubview(subview)
}
@objc func tapGestureRecognized(_ sender: UITapGestureRecognizer) {
guard sender.state == .ended, let tappedView = sender.view else { return }
var viewName: String
switch tappedView {
case view: viewName = "Root"
case subview: viewName = "Sub"
default: return
}
let location = sender.location(in: tappedView)
print("\n\(viewName) view tapped @\(location)")
if let layer = tappedView.layer.hitTest(location) {
print("Hit test returned <\(layer.name ?? "unknown")> with frame \(layer.frame)")
}
}
}
PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true
//: [Next](@next)
更新:已解决问题
在始终学识渊博的Matt Nueburg(和他最出色的书)的指导下,我将轻击手势处理程序方法修改为以下内容:
@objc func tapGestureRecognized(_ sender: UITapGestureRecognizer) {
guard sender.state == .ended, let tappedView = sender.view else { return }
var viewName: String
let hitTestLocation: CGPoint
switch tappedView {
case view:
viewName = "Root"
hitTestLocation = sender.location(in: tappedView)
case subview:
viewName = "Sub"
hitTestLocation = sender.location(in: tappedView.superview!)
default: return
}
print("\n\(viewName) view tapped @\(sender.location(in: tappedView))")
if let layer = tappedView.layer.hitTest(hitTestLocation) {
print("Hit test returned <\(layer.name ?? "unknown")> with frame \(layer.frame)")
}
}
现在控制台输出看起来不错:
点击根视图@(186.0,19.0)
命中测试返回带有帧(0.0、0.0、375.0、668.0)的“根视图->根层”
点击根视图@(187.0,45.0)
命中测试返回带有帧(0.0、0.0、375.0、668.0)的“根视图->根层”
点击根视图@(187.0,84.5)
命中测试返回带有帧(0.0、0.0、375.0、668.0)的“根视图->根层”
点击子视图@(138.0,9.0)
命中测试返回带有帧(50.0、100.0、275.0、468.0)的“子视图->根层”
点击子视图@(137.5,43.5)
命中测试返回带有帧(50.0、100.0、275.0、468.0)的“子视图->根层”
点击子视图@(138.5,77.5)
命中测试返回带有帧(50.0、100.0、275.0、468.0)的“子视图->根层”
点击子视图@(138.0,111.0)
命中测试返回带有帧(50.0、100.0、175.0、268.0)的“ Sub View-> Sub Layer 1”
点击子视图@(138.5,140.5)
命中测试返回带有帧(50.0、100.0、175.0、268.0)的“ Sub View-> Sub Layer 1”
点击子视图@(139.5,174.5)
命中测试返回带有帧(50.0、100.0、175.0、268.0)的“ Sub View-> Sub Layer 1”
第二次更新-清除代码
我回想起我最终用来检测子层上的触摸的代码。我希望它比上面的操场代码更清楚。
private struct LayerTouched : CustomStringConvertible {
let view: UIView // The touched view
var layer: CALayer // The touched layer
var location: CGPoint // The touch location expressed in the touched layer's coordinate system
init(by recognizer: UIGestureRecognizer) {
view = recognizer.view!
let gestureLocation = recognizer.location(in: view)
// AFAIK - Any touchable layer will have a super layer. Hence the forced unwrap is okay.
let hitTestLocation = view.layer.superlayer!.convert(gestureLocation, from: view.layer)
layer = view.layer.hitTest(hitTestLocation)!
location = layer.convert(gestureLocation, from: view.layer)
}
var description: String {
return """
Touched \(layer)
of \(view)
at \(location).
"""
}
}
答案 0 :(得分:1)
子视图的根层的
hitTest(:CGPoint)
方法
但是您忘记了层级点击测试以一种特殊的方式工作。您提供的CGPoint必须位于要进行测试的图层的超层的坐标系中。