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
}
}
@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