Search code examples
iosswiftuibezierpath

How to create Dashed line with Arrow in swift 4 with the help bezier path?


I want to create an arrow with a dashed line on user touching points.

This is my code. But I have not got any solution.

let arrow = UIBezierPath()
            arrow.addArrow(start: CGPoint(x: startTouch!.x, y: startTouch!.y), end: CGPoint(x: secondTouch!.x, y: secondTouch!.y), pointerLineLength: 20, arrowAngle: CGFloat(Double.pi / 5))
            self.currentContext?.setLineCap(.round)
            self.currentContext?.setLineJoin(.round)
            self.currentContext?.addPath(arrow.cgPath)

extension UIBezierPath {

    func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
        self.move(to: start)
        self.addLine(to: end)

        let startEndAngle = atan((end.y - start.y) / (end.x - start.x)) + ((end.x - start.x) < 0 ? CGFloat(Double.pi) : 0.0)
        let arrowLine1 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
        let arrowLine2 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))

        self.addLine(to: arrowLine1)
        self.move(to: end)
        self.addLine(to: arrowLine2)
    }    
}

enter image description here

I have attached a screenshot for requirement.


Solution

  • Please try the below code for your solution,

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        let arrow = UIBezierPath()
        let startTouchPoint = CGPoint(x: startTouch!.x, y: startTouch!.y) //CGPoint(x: 200, y: 200)
        let secondTouchPoint = CGPoint(x: secondTouch!.x, y: secondTouch!.y) //CGPoint(x: 50, y: 50)
        arrow.addArrow(start: startTouchPoint, end: secondTouchPoint, pointerLineLength: 20, arrowAngle: CGFloat(Double.pi / 5))
    
        let arrowLayer = CAShapeLayer()
        let path = CGMutablePath()
        arrowLayer.strokeColor = UIColor.red.cgColor
        arrowLayer.lineDashPattern = [7, 6]
        arrowLayer.lineWidth = 3
        path.addPath(arrow.cgPath)
        path.addLines(between: [startTouchPoint, secondTouchPoint])
        arrowLayer.path = path
    
        arrowLayer.fillColor = UIColor.clear.cgColor
        arrowLayer.lineJoin = CAShapeLayerLineJoin.round
        arrowLayer.lineCap = CAShapeLayerLineCap.round
    
        self.view.layer.addSublayer(arrowLayer)
    }
    
    extension UIBezierPath {
        func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
            self.move(to: start)
            self.addLine(to: end)
    
            let startEndAngle = atan((end.y - start.y) / (end.x - start.x)) + ((end.x - start.x) < 0 ? CGFloat(Double.pi) : 0)
            let arrowLine1 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
            let arrowLine2 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
    
            self.addLine(to: arrowLine1)
            self.move(to: end)
            self.addLine(to: arrowLine2)
        }
    }
    

    Output:

    output

    Test Data:

    let startTouchPoint = CGPoint(x: 200, y: 200)
    let secondTouchPoint = CGPoint(x: 50, y: 50)