-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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