Hit detection when drawing lines in iOS

前端 未结 2 766
终归单人心
终归单人心 2020-12-06 06:16

I would like to allow the user to draw curves in such a way that no line can cross another line or even itself. Drawing the curves is no problem, and I even found that I can

2条回答
  •  攒了一身酷
    2020-12-06 06:35

    Swift 4, answer is based on CGPath Hit Testing - Ole Begemann (2012)

    From Ole Begemann blog:

    contains(point: CGPoint)
    

    This function is helpful if you want to hit test on the entire region the path covers. As such, contains(point: CGPoint) doesn’t work with unclosed paths because those don’t have an interior that would be filled.

    copy(strokingWithWidth lineWidth: CGFloat, lineCap: CGLineCap, lineJoin: CGLineJoin, miterLimit: CGFloat, transform: CGAffineTransform = default) -> CGPath
    

    This function creates a mirroring tap target object that only covers the stroked area of the path. When the user taps on the screen, we iterate over the tap targets rather than the actual shapes.


    My solution in code

    I use a UITapGestureRecognizer linked to the function tap():

    var bezierPaths = [UIBezierPath]()   // containing all lines already drawn
    var tappedPaths = [CAShapeLayer]()
    
    @IBAction func tap(_ sender: UITapGestureRecognizer) {        
        let point = sender.location(in: imageView)
    
        for path in bezierPaths {
            // create tapTarget for path
            if let target = tapTarget(for: path) {
                if target.contains(point) {
                    tappedPaths.append(layer)
                }
            }
        }
    }
    
    fileprivate func tapTarget(for path: UIBezierPath) -> UIBezierPath {
    
        let targetPath = path.copy(strokingWithWidth: path.lineWidth, lineCap: path..lineCapStyle, lineJoin: path..lineJoinStyle, miterLimit: path.miterLimit)
    
        return UIBezierPath.init(cgPath: targetPath)
    }
    

提交回复
热议问题