我有一个CAShapeLayer
我已经将填充颜色标记为清晰。
当我点击该行时,它并不总是检测CAShapeLayer
cgpath
是否包含点击点。我的代码如下:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
guard let point = touch?.location(in: self) else { return }
for sublayer in self.layer.sublayers! {
if let l = sublayer as? CAShapeLayer {
if let path = l.path, path.contains(point) {
print("Tap detected")
}
}
}
}
所以我想把这条线从6到45变得非常胖。但它仍然不起作用。然后我想在此之后将填充变为灰色,现在当我点击填充灰色时它始终检测到水龙头。我真的很困惑,为什么它会检测到线上填充或线的中心,为什么不在线的整个厚度上。
答案 0 :(得分:0)
我认为问题在于CGPath.contains()
无法以您期望的方式运作。
来自Apple的CGPath docs:
讨论
如果路径填充时,点将包含在路径中,如果它将位于绘制区域内。
因此,该方法实际上并未检查您是否与绘制的线相交,它正在检查您是否与形状相交(即使您不是明确填写路径。)
一些基本实验表明,如果提供的点是
,则该方法返回true
CAShapeLayer
的{{1}}中的行的绘制轮廓)或lineWidth
可以创建实体形状的位置(就好像它已经关闭并填充)。您可能会在Stack Overflow上找到一些解决方法(似乎很多其他人之前遇到过相同的问题)。例如。 Hit detection when drawing lines in iOS
您可能还会发现此博文有用:CGPath Hit Testing - Ole Begemann。
答案 1 :(得分:0)
迅速4,答案基于说明,并链接至caseynolan在其他评论中的CGPath Hit Testing - Ole Begemann(2012):
来自Ole Begemann博客:
contains(point: CGPoint)
如果您想在整个区域进行测试,此功能很有用 路径覆盖。因此,包含(点:CGPoint) 未封闭的路径,因为那些内部没有 填充。
copy(strokingWithWidth lineWidth: CGFloat, lineCap: CGLineCap, lineJoin: CGLineJoin, miterLimit: CGFloat, transform: CGAffineTransform = default) -> CGPath
此函数创建一个镜像tapTarget对象,该对象仅包含 路径的描边区域。当用户点击屏幕时,我们 遍历敲击目标而不是实际形状。
我的代码解决方案
我使用链接到功能tap()的UITapGestureRecognizer:
var tappedLayers = [CAShapeLayer]()
@IBAction func tap(_ sender: UITapGestureRecognizer) {
let point = sender.location(in: imageView)
guard let sublayers = imageView.layer.sublayers as? [CAShapeLayer] else {
return
}
for layer in sublayers {
// create tapTarget for path
if let target = tapTarget(for: layer) {
if target.contains(point) {
tappedLayers.append(layer)
}
}
}
}
fileprivate func tapTarget(for layer: CAShapeLayer) -> UIBezierPath? {
guard let path = layer.path else {
return nil
}
let targetPath = path.copy(strokingWithWidth: layer.lineWidth, lineCap: CGLineCap.round, lineJoin: CGLineJoin.round, miterLimit: layer.miterLimit)
return UIBezierPath.init(cgPath: targetPath)
}