Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save casperzandbergenyaacomm/103ea0eae00350c54f15da4c68c28293 to your computer and use it in GitHub Desktop.
Save casperzandbergenyaacomm/103ea0eae00350c54f15da4c68c28293 to your computer and use it in GitHub Desktop.
Helper class to generate simple arrows images of desired size, colour and direction using UIKit api.
//
// ArrowImageGenerator.swift
//
// Created by Alessio Orlando on 07/10/15.
// Copyright © 2015 Alessio Orlando. All rights reserved.
//
import Foundation
import UIKit
enum ArrowDirection {
case up
case down
case left
case right
}
public class ArrowImageGenerator {
static var defaultColor: UIColor = UIColor(red: 0.783922, green: 0.780392, blue: 0.8, alpha: 1)
class func generateArrow(withDirection direction: ArrowDirection = .right,
size: CGSize? = nil,
lineWidth: CGFloat = 2.0,
lineCap: CGLineCap? = nil,
arrowColor: UIColor = defaultColor,
backgroundColor: UIColor = .clear,
scale: CGFloat = UIScreen.main.scale)
-> UIImage? {
let actualLineCap = lineCap ?? defaultLineCap(for: direction)
let actualSize = size ?? defaultSize(for: direction)
let scaledSize = actualSize.applying(CGAffineTransform(scaleX: scale, y: scale))
let scaledLineWidth = lineWidth * scale
UIGraphicsBeginImageContext(CGSize(width: scaledSize.width, height: scaledSize.height))
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else { return nil }
configureForArrowDrawing(context)
UIGraphicsPushContext(context)
strokeArrow(context, size: scaledSize, arrowColor: arrowColor, backgroundColor: backgroundColor, lineWidth: scaledLineWidth, lineCap: actualLineCap, direction: direction)
UIGraphicsPopContext()
guard let outputImage = UIGraphicsGetImageFromCurrentImageContext(),
let quartzImage = context.makeImage() else {
return nil
}
let scaledImage = UIImage(cgImage: quartzImage, scale: scale, orientation: outputImage.imageOrientation)
return scaledImage
}
private class func generateResizableArrow(_ arrowImage: UIImage, direction: ArrowDirection) -> UIImage {
var edgeInset: UIEdgeInsets?
switch direction {
case .up:
edgeInset = UIEdgeInsets(top: 11, left: 0, bottom: 1, right: 0)
case .down:
edgeInset = UIEdgeInsets(top: 1, left: 0, bottom: 11, right: 0)
case .left:
edgeInset = UIEdgeInsets(top: 1, left: 11, bottom: 1, right: 0)
case .right:
edgeInset = UIEdgeInsets(top: 1, left: 0, bottom: 1, right: 11)
}
let resizableImage = arrowImage.resizableImage(withCapInsets: edgeInset!)
return resizableImage
}
private class func configureForArrowDrawing(_ context: CGContext) {
context.setBlendMode(.normal)
context.setAllowsAntialiasing(true)
context.setShouldAntialias(true)
}
private class func strokeArrow(_ context: CGContext, size: CGSize, arrowColor: UIColor, backgroundColor: UIColor, lineWidth: CGFloat, lineCap: CGLineCap, direction: ArrowDirection) {
// Inset so the image doesnt cut off the line
// needed to make the arrow pointy and not look rounded
// 0.705 is the radius of a square: http://www.webmath.com/geo_square.html
let inset = lineWidth * 0.705
let sizeRect = CGRect(origin: .zero, size: size)
let insetSize = sizeRect.insetBy(dx: inset, dy: inset).size
context.translateBy(x: inset, y: inset)
backgroundColor.setFill()
UIRectFill(CGRect(origin: CGPoint(x: 0, y: 0), size: insetSize))
arrowColor.setStroke()
context.setLineWidth(lineWidth)
context.setLineCap(lineCap)
context.setLineJoin(lineJoin(for: lineCap))
switch direction {
case .up:
context.move(to: CGPoint(x: insetSize.width, y: insetSize.height))
context.addLine(to: CGPoint(x: insetSize.width / 2, y: 0))
context.addLine(to: CGPoint(x: 0, y: insetSize.height))
case .down:
context.move(to: CGPoint(x: insetSize.width, y: 0))
context.addLine(to: CGPoint(x: insetSize.width / 2, y: insetSize.height))
context.addLine(to: CGPoint(x: 0, y: 0))
case .left:
context.move(to: CGPoint(x: insetSize.width, y: 0))
context.addLine(to: CGPoint(x: 0, y: insetSize.height / 2))
context.addLine(to: CGPoint(x: insetSize.width, y: insetSize.height))
case .right:
context.move(to: CGPoint(x: 0, y: 0))
context.addLine(to: CGPoint(x: insetSize.width, y: insetSize.height / 2))
context.addLine(to: CGPoint(x: 0, y: insetSize.height))
}
context.strokePath()
}
class func defaultSize(for direction: ArrowDirection) -> CGSize {
switch direction {
case .up, .down:
return CGSize(width: 14, height: 8)
case .left, .right:
return CGSize(width: 8, height: 14)
}
}
private class func defaultLineCap(for direction: ArrowDirection) -> CGLineCap {
switch direction {
case .up, .down:
return .round
case .left, .right:
return .square
}
}
private class func lineJoin(for lineCap: CGLineCap) -> CGLineJoin {
switch lineCap {
case .square: return .miter
default: return .round
}
}
}
import PlaygroundSupport
let size = CGSize(width: 80, height: 140)
let view = UIImageView()
view.frame.size = size
view.contentMode = .scaleAspectFit
view.image = ArrowImageGenerator.generateArrow(size: size, lineWidth: 20)
view.backgroundColor = .white
PlaygroundPage.current.liveView = view
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment