Skip to content

Instantly share code, notes, and snippets.

@mwermuth
Created September 11, 2014 14:01
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mwermuth/07825df27ea28f5fc89a to your computer and use it in GitHub Desktop.
Save mwermuth/07825df27ea28f5fc89a to your computer and use it in GitHub Desktop.
Drawing an Arrow with Swift
extension UIBezierPath {
class func getAxisAlignedArrowPoints(inout points: Array<CGPoint>, forLength: CGFloat, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat ) {
let tailLength = forLength - headLength
points.append(CGPointMake(0, tailWidth/2))
points.append(CGPointMake(tailLength, tailWidth/2))
points.append(CGPointMake(tailLength, headWidth/2))
points.append(CGPointMake(forLength, 0))
points.append(CGPointMake(tailLength, -headWidth/2))
points.append(CGPointMake(tailLength, -tailWidth/2))
points.append(CGPointMake(0, -tailWidth/2))
}
class func transformForStartPoint(startPoint: CGPoint, endPoint: CGPoint, length: CGFloat) -> CGAffineTransform{
let cosine: CGFloat = (endPoint.x - startPoint.x)/length
let sine: CGFloat = (endPoint.y - startPoint.y)/length
return CGAffineTransformMake(cosine, sine, -sine, cosine, startPoint.x, startPoint.y)
}
class func bezierPathWithArrowFromPoint(startPoint:CGPoint, endPoint: CGPoint, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat) -> UIBezierPath {
let xdiff: Float = Float(endPoint.x) - Float(startPoint.x)
let ydiff: Float = Float(endPoint.y) - Float(startPoint.y)
let length = hypotf(xdiff, ydiff)
var points = [CGPoint]()
self.getAxisAlignedArrowPoints(&points, forLength: CGFloat(length), tailWidth: tailWidth, headWidth: headWidth, headLength: headLength)
var transform: CGAffineTransform = self.transformForStartPoint(startPoint, endPoint: endPoint, length: CGFloat(length))
var cgPath: CGMutablePathRef = CGPathCreateMutable()
CGPathAddLines(cgPath, &transform, points, 7)
CGPathCloseSubpath(cgPath)
var uiPath: UIBezierPath = UIBezierPath(CGPath: cgPath)
return uiPath
}
}
@mwermuth
Copy link
Author

Use it like this

let arrowPath = UIBezier. bezierPathWithArrowFromPoint(CGPointMake(0,0), CGPointMake(0,20), 4, 8, 6)

@boyxgc
Copy link

boyxgc commented Oct 3, 2014

Good job!

@arbel03
Copy link

arbel03 commented Apr 20, 2016

Perfect job 💯

@sivamurugan
Copy link

Excellent "mwermuth" Awesome work ... Helps me lot ... Please let me know this to draw in the real time drawing from one point to another point ... thanks in advance.

@gsujathap
Copy link

How to use it between views?

@lehuudungle
Copy link

how to draw curved arrow ???

@Akash20395
Copy link

Akash20395 commented Jan 12, 2018

let arrowPath = UIBezier. bezierPathWithArrowFromPoint(CGPointMake(0,0), CGPointMake(0,20), 4, 8, 6)

How overlay this arrowPath on UIImageView() ?

@jcfrank77
Copy link

Updated for Swift 5:

`extension UIBezierPath {
class func getAxisAlignedArrowPoints(points: inout [CGPoint], forLength: CGFloat, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat) {
let tailLength = forLength - headLength
points.append(CGPointMake(0, tailWidth / 2))
points.append(CGPointMake(tailLength, tailWidth / 2))
points.append(CGPointMake(tailLength, headWidth / 2))
points.append(CGPointMake(forLength, 0))
points.append(CGPointMake(tailLength, -headWidth / 2))
points.append(CGPointMake(tailLength, -tailWidth / 2))
points.append(CGPointMake(0, -tailWidth / 2))
}

class func transformForStartPoint(startPoint: CGPoint, endPoint: CGPoint, length: CGFloat) -> CGAffineTransform {
    let cosine: CGFloat = (endPoint.x - startPoint.x) / length
    let sine: CGFloat = (endPoint.y - startPoint.y) / length

    return CGAffineTransformMake(cosine, sine, -sine, cosine, startPoint.x, startPoint.y)
}

class func bezierPathWithArrowFromPoint(startPoint: CGPoint,
                                        endPoint: CGPoint,
                                        tailWidth: CGFloat,
                                        headWidth: CGFloat,
                                        headLength: CGFloat) -> UIBezierPath
{
    let xdiff = Float(endPoint.x) - Float(startPoint.x)
    let ydiff = Float(endPoint.y) - Float(startPoint.y)
    let length = hypotf(xdiff, ydiff)

    var points = [CGPoint]()
    getAxisAlignedArrowPoints(points: &points, forLength: CGFloat(length), tailWidth: tailWidth, headWidth: headWidth, headLength: headLength)

    let transform: CGAffineTransform = transformForStartPoint(startPoint: startPoint, endPoint: endPoint, length: CGFloat(length))

    let cgPath = CGMutablePath()
    cgPath.addLines(between: points, transform: transform)
    cgPath.closeSubpath()

    let uiPath = UIBezierPath(cgPath: cgPath)
    return uiPath
}

}`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment