Skip to content

Instantly share code, notes, and snippets.

@trs123
Last active June 5, 2016 12:14
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 trs123/e65a4b6430af327796c1c8769f14ca00 to your computer and use it in GitHub Desktop.
Save trs123/e65a4b6430af327796c1c8769f14ca00 to your computer and use it in GitHub Desktop.
import UIKit
// Swift rewrite challenge by Erica Sadun: http://ericasadun.com/2016/05/31/swift-rewrite-challenge/
// Starting point: https://gist.github.com/erica/157e20ea0c7e9f28a03a8b12448c8fd0
// My solution builds on a small framework I have written with some geometry extensions on CGVector, CGPoint, CGRect and CGSize
// I call it GeometryKit and include the relevant parts here. This makes geometric calculations so much easier to understand.
// ------------------------------------------------------------------------------------
// GeometryKit
// ------------------------------------------------------------------------------------
extension CGVector {
/// Vector with zero length
public static func zero() -> CGVector {
return CGVector()
}
/// Vector pointing in same direction as receiver but scale times as long
public func scaledBy(scale: CGFloat) -> CGVector {
return CGVector(dx: dx * scale, dy: dy * scale)
}
/// Vector pointing in same direction as receiver but half as long
public var half: CGVector {
return scaledBy(0.5)
}
/// Vector of same length as receiver but pointing in opposite direction
public var negated: CGVector {
return CGVector(dx: -dx, dy: -dy)
}
}
/// Sum of two vectors
public func +(v1: CGVector, v2: CGVector) -> CGVector {
return CGVector(dx: v1.dx + v2.dx, dy: v1.dy + v2.dy)
}
/// Difference of two vectors
public func -(v1: CGVector, v2: CGVector) -> CGVector {
return CGVector(dx: v1.dx - v2.dx, dy: v1.dy - v2.dy)
}
public extension CGPoint {
/// CGVector from origin to receiver
public func asVector() -> CGVector {
return CGVector(dx: x, dy: y)
}
/// Point offset from receiver by vector
public func offsetBy(v: CGVector) -> CGPoint {
return CGPoint(x: x + v.dx, y: y + v.dy)
}
/// Vector from receiver to other point
public func vectorTo(other: CGPoint) -> CGVector {
return other.asVector() - asVector()
}
}
public extension CGRect {
/// Initialize Rect from center location and size
public init(center: CGPoint, size: CGSize) {
self.init(origin: center.offsetBy(size.asVector().half.negated), size: size)
}
/// Initialize Rect from size
public init(size: CGSize) {
self.init(origin: .zero, size: size)
}
/// Location of receiver's center
public var center: CGPoint {
return origin.offsetBy(size.asVector().half)
}
}
public extension CGSize {
/// Convert receiver to vector
public func asVector() -> CGVector {
return CGVector(dx: width, dy: height)
}
/// Scaled by factor
public func scaledBy(scale: CGFloat) -> CGSize {
return CGSize(width: width * scale, height: height * scale)
}
/// Size of receiver scaled to fit the given size
public func aspectFit(size: CGSize) -> CGSize {
let scaleX = size.width / width
let scaleY = size.height / height
let scale = min(scaleX, scaleY)
return scaledBy(scale)
}
/// Size of receiver scaled to fill the given size
public func aspectFill(size: CGSize) -> CGSize {
let scaleX = size.width / width
let scaleY = size.height / height
let scale = max(scaleX, scaleY)
return scaledBy(scale)
}
}
public extension UIImage {
public var bounds: CGRect { return CGRect(origin: .zero, size: size) }
}
/// Draw on graphics context
func drawImageWithOptions(size: CGSize, opaque: Bool, scale: CGFloat, draw: (withinBounds: CGRect) -> ()) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
let bounds = CGRect(size: size)
draw(withinBounds: bounds)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
// ------------------------------------------------------------------------------------
// My Scaling Function
// ------------------------------------------------------------------------------------
/// Scaling mode for scaling CGSize values
enum ScalingMode {
case fit
case fill
case unscaled
func scale(size: CGSize, within targetSize: CGSize) -> CGSize {
switch self {
case .fit: return size.aspectFit(targetSize)
case .fill: return size.aspectFill(targetSize)
case .unscaled: return size
}
}
}
/// Scale image to given size using given scaling mode (default: .fit)
func scaleImage(
image: UIImage,
toSize size: CGSize,
scalingMode: ScalingMode = .fit
) -> UIImage {
// Return original when cropping is not needed
guard image.size != size else { return image }
// Calculate scaled size for scaling mode
let scaledSize = scalingMode.scale(image.size, within: size)
// Perform drawing and return image
return drawImageWithOptions(size, opaque: false, scale: 0.0) { bounds in
// Fill background
UIColor.blackColor().setFill(); UIRectFill(bounds)
// Draw scaled image centered within bounds
image.drawInRect(CGRect(center: bounds.center, size: scaledSize))
}
}
// Test with some basic placeholder data
guard let url = NSURL(string: "http://placehold.it/300x150") else { fatalError("Bad URL") }
guard let data = NSData(contentsOfURL: url) else { fatalError("Bad data") }
guard let img = UIImage(data: data) else { fatalError("Bad data") }
let outImageFit = scaleImage(img, toSize: CGSize(width: 200, height: 200))
let outImageFill = scaleImage(img, toSize: CGSize(width: 200, height: 200), scalingMode: .fill)
let outImageCentered = scaleImage(img, toSize: CGSize(width: 200, height: 200), scalingMode: .unscaled)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment