CAShapeLayer。线是否通过该点?

时间:2017-08-06 13:05:07

标签: ios swift swift3 swift2

我使用CAShapeLayer在屏幕上画一条线。在方法 touchesEnded 我想检查"线是否通过该点?"。在我的代码中,当我按下屏幕的任何部分时,方法包含将始终返回true。也许,我在 line.frame =(view?.bounds)中遇到问题!。我该如何解决?  抱歉我的英语不好。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    let touch = touches.first
    let firstPosition = touch?.location(in: self)

    if atPoint(firstPosition!) == lvl1 {

        let firstPositionX = firstPosition?.x
        let firstPositionY = frame.size.height - (firstPosition?.y)!
        view?.layer.addSublayer(line)
        line.lineWidth = 8
        let color = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1).cgColor
        line.strokeColor = color
        line.fillColor = nil
        line.frame = (view?.bounds)!
        path.move(to: CGPoint(x: firstPositionX!, y: firstPositionY))

    }

}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    let touch = touches.first
    let firstPosition = touch?.location(in: self)

    if atPoint(firstPosition!) == lvl1 {

        let firstPositionX = firstPosition?.x
        let firstPositionY = frame.size.height - (firstPosition?.y)!
        path.addLine(to: CGPoint(x: firstPositionX!, y: firstPositionY))
        line.path = path.cgPath

    }
}


override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

    if line.contains(screenCenterPoint) {
        print("ok")
    }

}

1 个答案:

答案 0 :(得分:5)

问题

如果图层的边界包含该点,则func contains(_ p: CGPoint) -> Bool的方法CAShapeLayer会返回true。 (see documentation

所以你不能用它来检查是否包含一个点。

然而,在类CGPath中有另一个具有相同名称的方法,它返回指定的点是内部到路径。但由于你只有描边你的路径并且你没有填充内部,这种方法也不会给出所需的结果。

解决方案

诀窍是使用以下方法创建路径的大纲

let outline = path.cgPath.copy(strokingWithWidth: line.lineWidth, lineCap: .butt, lineJoin: .round, miterLimit: 0)

然后检查大纲内部是否包含screenCenterPoint

if outline.contains(screenCenterPoint) {
    print("ok")
}

Stroke vs outline

性能注意事项

由于您仅在触及结束时检查包含,我认为创建路径大纲不会增加太多开销。

如果要检查实时中的包含,例如在touchesMoved函数内,计算大纲可能会产生一些开销,因为此方法每秒调用很多次。此外,路径越长,计算轮廓所需的时间就越长。

因此,实时最好只生成最后绘制的线段的轮廓,然后检查该轮廓是否包含您的观点。

如果您想严肃地减少开销,可以编写自己的包含功能。直线点的遏制相当简单,可以简化为以下公式:

  

startend的{​​{1}}和width

     

计算:

     
      
  • p
  •   
  • dx = start.x - end.x
  •   
  • dy = start.y - end.y
  •   
  • a = dy * p.x - dx * p.y + end.x * start.y - end.y * start.x
  •   
     

如果符合以下情况,则该行包含点b = hypot(dy, dx)

     

p p位于该行的边界框中。