Search code examples
iosswiftswift3cashapelayer

Swift UIView: make a path always be at the end of person's finger?


OK, I am new to UIBezierPaths but I need to have a path whose endpoint updates according to where the user's finger is. It should change in touchesMoved

So far I have:

func customInit() {
    print("init scene")
    self.backgroundColor = UIColor.cyan

    path.move(to: CGPoint(x: 0, y: 0))
    path.addLine(to: CGPoint(x: 300, y: 300))

    let shapeLayer = CAShapeLayer()
    shapeLayer.path = path.cgPath
    shapeLayer.strokeColor = UIColor.blue.cgColor
    shapeLayer.lineWidth = 3.0

    self.layer.addSublayer(shapeLayer)
}

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

    var touch : UITouch! =  touches.first! as UITouch
    var location = touch.location(in: self)

    path.move(to: location)
}

I thought this would update the path endpoint but nothing happens beyond drawing the original line. I would like to not use SKScene for this, but don't know what is going wrong.

How can I make a line always have a point at the point of user's tap?


Solution

  • You're very close! You're missing just a few things.

    When touchesMoved is called, you need to update the path and re-assign it to the CAShapeLayer so that it can be re-drawn. Right now you are just making a modification to the path object, but not re-drawing it.

    Final Code

    class DrawingView: UIView {
    
        let startingPoint = CGPoint(x: 0, y: 0)
    
        let shapeLayer = CAShapeLayer()
        let path = UIBezierPath()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            customInit()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            customInit()
        }
    
        func customInit() {
    
            backgroundColor = UIColor.cyan
    
            path.move(to: startingPoint)
            path.addLine(to: CGPoint(x: 50, y: 50))
    
            shapeLayer.path = path.cgPath
            shapeLayer.strokeColor = UIColor.blue.cgColor
            shapeLayer.lineWidth = 3.0
    
            layer.addSublayer(shapeLayer)
    
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesMoved(touches, with: event)
    
            let touch =  touches.first as! UITouch
            let location = touch.location(in: self)
    
            path.removeAllPoints()
            path.move(to: startingPoint)
            path.addLine(to: location)
    
            shapeLayer.path = path.cgPath
    
        }
    
    }
    

    Final Result

    gif of the line endpoint moving on touch

    (tested in an Xcode 8.3.2 playground)


    Update:

    To make the line disappear when the touch is released, use touchesEnded. Inside this method, simply remove the points on the path and reassign it to the shapeLayer so the view is updated.

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        path.removeAllPoints()
        shapeLayer.path = path.cgPath
    }