From 2dc2ac0c3b273035a2b77bb3fe905f84f7ed726f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B8=CC=86=20=D0=90?= =?UTF-8?q?=D1=88=D0=B0=D0=BD=D0=B8=D0=BD?= Date: Thu, 29 Sep 2016 15:56:39 +0300 Subject: [PATCH] image extension added --- LeadKit/.DS_Store | Bin 6148 -> 6148 bytes LeadKit/LeadKit.xcodeproj/project.pbxproj | 36 ++++- .../Classes/Cache/ObjectsGenerator.swift | 6 +- .../Classes/Cache/ViewsGenerator.swift | 4 +- .../AlamofireRequest+Extensions.swift | 4 +- .../Extensions/UIColor/UIColor+Hex.swift | 18 +-- .../Extensions/UIImage/UIImage+Alpha.swift | 123 ++++++++++++++++++ .../UIImage/UIImage+CapInsetsUtils.swift | 2 +- .../Extensions/UIImage/UIImage+Creation.swift | 21 +++ .../Extensions/UIImage/UIImage+Cropping.swift | 44 +++++++ .../UIImage/UIImage+Gradients.swift | 122 +++++++++++++++++ .../Extensions/UIImage/UIImage+Loading.swift | 85 ++++++++++++ .../Extensions/UIImage/UIImage+Resize.swift | 49 +++++++ .../Extensions/UIImage/UIImage+Text.swift | 45 +++++++ .../UIImage/UIImage+Transformations.swift | 119 +++++++++++++++++ .../UIImageView+LoadingImage.swift | 51 ++++++++ 16 files changed, 703 insertions(+), 26 deletions(-) create mode 100644 LeadKit/LeadKit/Extensions/UIImage/UIImage+Alpha.swift create mode 100644 LeadKit/LeadKit/Extensions/UIImage/UIImage+Cropping.swift create mode 100644 LeadKit/LeadKit/Extensions/UIImage/UIImage+Gradients.swift create mode 100644 LeadKit/LeadKit/Extensions/UIImage/UIImage+Loading.swift create mode 100644 LeadKit/LeadKit/Extensions/UIImage/UIImage+Text.swift create mode 100644 LeadKit/LeadKit/Extensions/UIImageView/UIImageView+LoadingImage.swift diff --git a/LeadKit/.DS_Store b/LeadKit/.DS_Store index ff34d0557148e086cf7266146c6336d809da14ba..36540d453b4d9c36e0702f3ad79d5fc0e927cb19 100644 GIT binary patch delta 61 zcmZoMXffDe&&bTkD?ND~qZ(gszKcsrX-P5z!xf=@+>_TZYD}KT$i;F+u+<*OG28rA Qgr9j6lNj@6c85CL(voBbhRc^-geI?H)R;Vvk&ET>W#?l+jv1rC S=8ueGY!e&!HnVg5 { /** initializer function - - parameter poolSize: number of objects to generate - - parameter contructor: objects constructor closure - - - returns: nothing + - parameter poolSize: number of objects to generate + - parameter contructor: objects constructor closure */ init(poolSize: UInt, objectsContructor contructor: ObjectConstructor) { self.poolSize = poolSize diff --git a/LeadKit/LeadKit/Classes/Cache/ViewsGenerator.swift b/LeadKit/LeadKit/Classes/Cache/ViewsGenerator.swift index 352bc3de..5b7bef95 100644 --- a/LeadKit/LeadKit/Classes/Cache/ViewsGenerator.swift +++ b/LeadKit/LeadKit/Classes/Cache/ViewsGenerator.swift @@ -14,9 +14,7 @@ public class ViewsGenerator: ObjectsGenerator { initializer function - parameter poolSize: number of cells to generate - - parameter nibName: view nib name - - - returns: nothing + - parameter nibName: view nib name */ init(poolSize: UInt, nibName: String) { super.init(poolSize: poolSize, objectsContructor: { T.loadFromNib(named: nibName) }) diff --git a/LeadKit/LeadKit/Extensions/Alamofire/AlamofireRequest+Extensions.swift b/LeadKit/LeadKit/Extensions/Alamofire/AlamofireRequest+Extensions.swift index 7ab3ec86..af933a9f 100644 --- a/LeadKit/LeadKit/Extensions/Alamofire/AlamofireRequest+Extensions.swift +++ b/LeadKit/LeadKit/Extensions/Alamofire/AlamofireRequest+Extensions.swift @@ -24,8 +24,8 @@ public extension Alamofire.Request { return .Failure(.Network(error: err)) } - let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments) - let result = JSONResponseSerializer.serializeResponse(request, response, data, error) + let jsonResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments) + let result = jsonResponseSerializer.serializeResponse(request, response, data, error) switch result { case .Success(let value): diff --git a/LeadKit/LeadKit/Extensions/UIColor/UIColor+Hex.swift b/LeadKit/LeadKit/Extensions/UIColor/UIColor+Hex.swift index 9c869ade..1ef9f821 100644 --- a/LeadKit/LeadKit/Extensions/UIColor/UIColor+Hex.swift +++ b/LeadKit/LeadKit/Extensions/UIColor/UIColor+Hex.swift @@ -11,13 +11,11 @@ import UIKit public extension UIColor { /** - The shorthand three-digit hexadecimal representation of color. + the shorthand three-digit hexadecimal representation of color. #RGB defines to the color #RRGGBB. - parameter hex3: Three-digit hexadecimal value. - parameter alpha: 0.0 - 1.0. The default is 1.0. - - - returns: new instance with given three-digit hexadecimal value */ public convenience init(hex3: UInt16, alpha: CGFloat = 1) { let red = CGFloat((hex3 & 0xF00) >> 8) / 0xF @@ -27,12 +25,10 @@ public extension UIColor { } /** - The shorthand four-digit hexadecimal representation of color with alpha. + the shorthand four-digit hexadecimal representation of color with alpha. #RGBA defines to the color #RRGGBBAA. - parameter hex4: Four-digit hexadecimal value. - - - returns: new instance with given four-digit hexadecimal value */ public convenience init(hex4: UInt16) { let red = CGFloat((hex4 & 0xF000) >> 12) / 0xF @@ -44,12 +40,10 @@ public extension UIColor { } /** - The six-digit hexadecimal representation of color of the form #RRGGBB. + the six-digit hexadecimal representation of color of the form #RRGGBB. - parameter hex6: Six-digit hexadecimal value. - parameter alpha: alpha: 0.0 - 1.0. The default is 1.0. - - - returns: new instance with given six-digit hexadecimal value */ public convenience init(hex6: UInt32, alpha: CGFloat = 1) { let red = CGFloat((hex6 & 0xFF0000) >> 16) / 0xFF @@ -60,11 +54,9 @@ public extension UIColor { } /** - The six-digit hexadecimal representation of color with alpha of the form #RRGGBBAA. + the six-digit hexadecimal representation of color with alpha of the form #RRGGBBAA. - parameter hex8: Eight-digit hexadecimal value. - - - returns: new instance with given eight-digit hexadecimal value */ public convenience init(hex8: UInt32) { let red = CGFloat((hex8 & 0xFF000000) >> 24) / 0xFF @@ -81,8 +73,6 @@ public extension UIColor { - parameter hexString: hex string with red green and blue values (can have `#` sign) - parameter alpha: alpha component used if not given in hexString - - - returns: new instance with given hex color or nil if hexString is incorrect */ public convenience init?(hexString: String, alpha: CGFloat = 1) { let hexStr = hexString.hasPrefix("#") ? hexString.substringFromIndex(hexString.startIndex.advancedBy(1)) : hexString diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Alpha.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Alpha.swift new file mode 100644 index 00000000..bb23247e --- /dev/null +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Alpha.swift @@ -0,0 +1,123 @@ +// +// UIImage+Alpha.swift +// LeadKit +// +// Created by Николай Ашанин on 28.09.16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit + +public extension UIImage { + + /** + - returns: true if the image has an alpha layer. + */ + public func hasAlpha() -> Bool { + let alpha = CGImageGetAlphaInfo(CGImage) + switch alpha { + case .First, .Last, .PremultipliedFirst, .PremultipliedLast: + return true + default: + return false + } + } + + /** + - returns: a copy of the given image, adding an alpha channel if it doesn't already have one. + */ + public func applyAlpha() -> UIImage? { + if hasAlpha() { + return self + } + + let imageRef = CGImage + let width = CGImageGetWidth(imageRef) + let height = CGImageGetHeight(imageRef) + let colorSpace = CGImageGetColorSpace(imageRef) + + // The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error + let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.ByteOrderDefault.rawValue | + CGImageAlphaInfo.PremultipliedFirst.rawValue) + let offscreenContext = CGBitmapContextCreate(nil, width, height, 8, 0, colorSpace, bitmapInfo.rawValue) + + // Draw the image into the context and retrieve the new image, which will now have an alpha layer + CGContextDrawImage(offscreenContext, + CGRect(x: 0, y: 0, width: width, height: height), + imageRef) + guard let cgImage = CGBitmapContextCreateImage(offscreenContext) else { + return nil + } + let imageWithAlpha = UIImage(CGImage: cgImage) + return imageWithAlpha + } + + /** + returns a copy of the image with a transparent border of the given size added around its edges. + i.e. For rotating an image without getting jagged edges. + + - parameter padding: The padding amount. + + - returns: A new image. + */ + public func applyPadding(padding: CGFloat) -> UIImage? { + // If the image does not have an alpha layer, add one + guard let image = applyAlpha() else { + return nil + } + let rect = CGRect(x: 0, y: 0, width: size.width + padding * 2, height: size.height + padding * 2) + + // Build a context that's the same dimensions as the new size + let colorSpace = CGImageGetColorSpace(CGImage) + let bitmapInfo = CGImageGetBitmapInfo(CGImage) + let bitsPerComponent = CGImageGetBitsPerComponent(CGImage) + let context = CGBitmapContextCreate(nil, + Int(rect.size.width), + Int(rect.size.height), + bitsPerComponent, 0, colorSpace, + bitmapInfo.rawValue) + + // Draw the image in the center of the context, leaving a gap around the edges + let imageLocation = CGRect(x: padding, y: padding, width: image.size.width, height: image.size.height) + CGContextDrawImage(context, imageLocation, CGImage) + + // Create a mask to make the border transparent, and combine it with the image + let imageWithPadding = imageRefWithPadding(padding, size: rect.size) + guard let cgImage = CGImageCreateWithMask(CGBitmapContextCreateImage(context), imageWithPadding) else { + return nil + } + let transparentImage = UIImage(CGImage: cgImage) + return transparentImage + } + + /** + creates a mask that makes the outer edges transparent and everything else opaque. + The size must include the entire mask (opaque part + transparent border). + + - parameter padding: The padding amount. + - parameter size: The size of the image. + + - returns: A Core Graphics Image Ref + */ + private func imageRefWithPadding(padding: CGFloat, + size: CGSize) -> CGImageRef? { + // Build a context that's the same dimensions as the new size + let colorSpace = CGColorSpaceCreateDeviceGray() + let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.ByteOrderDefault.rawValue | CGImageAlphaInfo.None.rawValue) + let context = CGBitmapContextCreate(nil, Int(size.width), Int(size.height), 8, 0, colorSpace, bitmapInfo.rawValue) + // Start with a mask that's entirely transparent + CGContextSetFillColorWithColor(context, UIColor.blackColor().CGColor) + CGContextFillRect(context, CGRect(x: 0, y: 0, width: size.width, height: size.height)) + // Make the inner part (within the border) opaque + CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor) + let fillRect = CGRect(x: padding, + y: padding, + width: size.width - padding * 2, + height: size.height - padding * 2) + CGContextFillRect(context, fillRect) + // Get an image of the context + let maskImageRef = CGBitmapContextCreateImage(context) + return maskImageRef + } + +} diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+CapInsetsUtils.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+CapInsetsUtils.swift index 80d4d84e..6883fb60 100644 --- a/LeadKit/LeadKit/Extensions/UIImage/UIImage+CapInsetsUtils.swift +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+CapInsetsUtils.swift @@ -40,7 +40,7 @@ public extension UIImage { } /** - this metho tries to find requested image with specific parameters in cache, and if doesn't exists - create it + this method tries to find requested image with specific parameters in cache, and if doesn't exists - create it - parameter name: name of the image used in UIImage(named:) - parameter size: size of rendered image diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Creation.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Creation.swift index 7ca919a9..9417d85a 100644 --- a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Creation.swift +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Creation.swift @@ -33,4 +33,25 @@ public extension UIImage { return UIGraphicsGetImageFromCurrentImageContext() } + /** + creates an image from a UIView. + + - parameter fromView: The source view. + + - returns A new image + */ + public convenience init?(fromView view: UIView) { + UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0) + + guard let context = UIGraphicsGetCurrentContext() else { + return nil + } + view.layer.renderInContext(context) + guard let cgImage = UIGraphicsGetImageFromCurrentImageContext().CGImage else { + return nil + } + self.init(CGImage: cgImage) + UIGraphicsEndImageContext() + } + } diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Cropping.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Cropping.swift new file mode 100644 index 00000000..7b3ed8e5 --- /dev/null +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Cropping.swift @@ -0,0 +1,44 @@ +// +// UIImage+Cropping.swift +// LeadKit +// +// Created by Николай Ашанин on 28.09.16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit + +public extension UIImage { + + /** + creates a cropped copy of an image. + + - parameter bounds: The bounds of the rectangle inside the image. + + - returns: A new image + */ + public func crop(bounds: CGRect) -> UIImage? { + guard let cgImage = CGImageCreateWithImageInRect(CGImage, bounds) else { + return nil + } + return UIImage(CGImage: cgImage, + scale: 0.0, + orientation: imageOrientation) + } + + /** + crop image to square + + - returns: cropped image + */ + public func cropToSquare() -> UIImage? { + let newSize = CGSize(width: size.width * scale, height: size.height * scale) + let shortest = min(newSize.width, newSize.height) + let left: CGFloat = newSize.width > shortest ? (newSize.width-shortest)/2 : 0 + let top: CGFloat = newSize.height > shortest ? (newSize.height-shortest)/2 : 0 + let rect = CGRect(x: 0, y: 0, width: size.width, height: newSize.height) + let insetRect = CGRectInset(rect, left, top) + return crop(insetRect) + } + +} diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Gradients.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Gradients.swift new file mode 100644 index 00000000..09f15ec4 --- /dev/null +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Gradients.swift @@ -0,0 +1,122 @@ +// +// UIImage+Gradients.swift +// LeadKit +// +// Created by Николай Ашанин on 28.09.16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit + +public extension UIImage { + + /** + creates a gradient color image. + + - parameter gradientColors: An array of colors to use for the gradient. + - parameter size: Image size (defaults: 10x10) + */ + public convenience init?(gradientColors: [UIColor], + size: CGSize = CGSize(width: 10, height: 10)) { + UIGraphicsBeginImageContextWithOptions(size, false, 0) + let context = UIGraphicsGetCurrentContext() + let colorSpace = CGColorSpaceCreateDeviceRGB() + let colors = gradientColors.map {(color: UIColor) -> AnyObject? in return color.CGColor as AnyObject? } as NSArray + let gradient = CGGradientCreateWithColors(colorSpace, colors, nil) + CGContextDrawLinearGradient(context, gradient, + CGPoint(x: 0, y: 0), + CGPoint(x: 0, y: size.height), + CGGradientDrawingOptions(rawValue: 0)) + guard let cgImage = UIGraphicsGetImageFromCurrentImageContext().CGImage else { + return nil + } + self.init(CGImage: cgImage) + UIGraphicsEndImageContext() + } + + /** + applies gradient color overlay to an image. + + - parameter gradientColors: An array of colors to use for the gradient. + - parameter blendMode: The blending type to use. + + - returns: A new image + */ + public func applyGradientColors(gradientColors: [UIColor], + blendMode: CGBlendMode = .Normal) -> UIImage { + UIGraphicsBeginImageContextWithOptions(size, false, scale) + let context = UIGraphicsGetCurrentContext() + CGContextTranslateCTM(context, 0, size.height) + CGContextScaleCTM(context, 1.0, -1.0) + CGContextSetBlendMode(context, blendMode) + let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) + CGContextDrawImage(context, rect, CGImage) + // Create gradient + let colorSpace = CGColorSpaceCreateDeviceRGB() + let colors = gradientColors.map {(color: UIColor) -> AnyObject? in return color.CGColor as AnyObject? } as NSArray + let gradient = CGGradientCreateWithColors(colorSpace, colors, nil) + // Apply gradient + CGContextClipToMask(context, rect, CGImage) + CGContextDrawLinearGradient(context, + gradient, + CGPoint(x: 0, y: 0), + CGPoint(x: 0, y: size.height), + CGGradientDrawingOptions(rawValue: 0)) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image + } + + /** + creates a radial gradient. + + - parameter startColor: The start color + - parameter endColor: The end color + - parameter radialGradientCenter: The gradient center (default:0.5,0.5). + - parameter radius: Radius size (default: 0.5) + - parameter size: Image size (default: 100x100) + */ + public convenience init?(startColor: UIColor, + endColor: UIColor, + radialGradientCenter: CGPoint = CGPoint(x: 0.5, y: 0.5), + radius: Float = 0.5, + size: CGSize = CGSize(width: 100, height: 100)) { + UIGraphicsBeginImageContextWithOptions(size, true, 0) + + let numLocations: Int = 2 + let locations: [CGFloat] = [0.0, 1.0] as [CGFloat] + + let startComponents = CGColorGetComponents(startColor.CGColor) + let endComponents = CGColorGetComponents(endColor.CGColor) + + let components: [CGFloat] = [startComponents[0], + startComponents[1], + startComponents[2], + startComponents[3], + endComponents[0], + endComponents[1], + endComponents[2], + endComponents[3]] as [CGFloat] + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, numLocations) + + // Normalize the 0-1 ranged inputs to the width of the image + let aCenter = CGPoint(x: radialGradientCenter.x * size.width, + y: radialGradientCenter.y * size.height) + let aRadius = CGFloat(min(size.width, size.height)) * CGFloat(radius) + + // Draw it + CGContextDrawRadialGradient(UIGraphicsGetCurrentContext(), + gradient, aCenter, 0, + aCenter, aRadius, + CGGradientDrawingOptions.DrawsAfterEndLocation) + guard let cgImage = UIGraphicsGetImageFromCurrentImageContext().CGImage else { + return nil + } + self.init(CGImage: cgImage) + // Clean up + UIGraphicsEndImageContext() + } + +} diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Loading.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Loading.swift new file mode 100644 index 00000000..ef329849 --- /dev/null +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Loading.swift @@ -0,0 +1,85 @@ +// +// UIImage+Loading.swift +// LeadKit +// +// Created by Николай Ашанин on 28.09.16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit +import QuartzCore +import CoreGraphics +import Accelerate + +public enum UIImageContentMode { + case ScaleToFill, ScaleAspectFit, ScaleAspectFill +} + +struct StaticSharedCache { + static var sharedCache: NSCache? = nil + static var onceToken: dispatch_once_t = 0 +} + +public extension UIImage { + + /** + a singleton shared NSURL cache used for images from URL + + - returns: shared image cache + */ + private class func sharedCache() -> NSCache { + dispatch_once(&StaticSharedCache.onceToken) { + StaticSharedCache.sharedCache = NSCache() + } + return StaticSharedCache.sharedCache + } + + // MARK: Image From URL + + /** + creates a new image from a URL with optional caching. + if cached, the cached image is returned. + otherwise, a place holder is used until the image from web is returned by the closure. + + - parameter url: The image URL. + - parameter placeholder: The placeholder image. + - parameter shouldCacheImage: Weather or not we should cache the NSURL response (default: true) + - parameter closure: Returns the image from the web the first time is fetched. + + - returns: A new image + */ + public class func imageFromURL(url: String, + placeholder: UIImage, + shouldCacheImage: Bool = true, + closure: (image: UIImage?) -> ()) -> UIImage? { + // From Cache + if shouldCacheImage { + if let image = UIImage.sharedCache().objectForKey(url) as? UIImage { + closure(image: nil) + return image + } + } + // Fetch Image + let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) + if let nsURL = NSURL(string: url) { + session.dataTaskWithURL(nsURL, completionHandler: { (data, response, error) -> Void in + if error != nil { + dispatch_async(dispatch_get_main_queue()) { + closure(image: nil) + } + } + if let data = data, image = UIImage(data: data) { + if shouldCacheImage { + UIImage.sharedCache().setObject(image, forKey: url) + } + dispatch_async(dispatch_get_main_queue()) { + closure(image: image) + } + } + session.finishTasksAndInvalidate() + }).resume() + } + return placeholder + } + +} diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Resize.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Resize.swift index 4acae7da..1e9a2213 100644 --- a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Resize.swift +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Resize.swift @@ -28,4 +28,53 @@ public extension UIImage { return UIGraphicsGetImageFromCurrentImageContext() } + /** + creates a resized copy of an image. + + - parameter size: the new size of the image. + - parameter contentMode: the way to handle the content in the new size. + + - returns: a new image + */ + public func resize(size: CGSize, + contentMode: UIImageContentMode = .ScaleToFill) -> UIImage? { + let horizontalRatio = size.width/size.width + let verticalRatio = size.height/size.height + var ratio: CGFloat = 1 + + switch contentMode { + case .ScaleToFill: + ratio = 1 + case .ScaleAspectFill: + ratio = max(horizontalRatio, verticalRatio) + case .ScaleAspectFit: + ratio = min(horizontalRatio, verticalRatio) + } + + let rect = CGRect(x: 0, y: 0, width: size.width * ratio, height: size.height * ratio) + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue) + let context = CGBitmapContextCreate(nil, + Int(rect.size.width), + Int(rect.size.height), 8, 0, colorSpace, + bitmapInfo.rawValue) + let transform = CGAffineTransformIdentity + CGContextConcatCTM(context, transform) + // Set the quality level to use when rescaling + guard let interpolationQuality = CGInterpolationQuality(rawValue: 3) else { + return nil + } + CGContextSetInterpolationQuality(context, interpolationQuality) + CGContextDrawImage(context, rect, CGImage) + + guard let newContext = context else { + return nil + } + let newImage = UIImage(CGImage: CGBitmapContextCreateImage(newContext), + scale: scale, + orientation: imageOrientation) + return newImage + } + } diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Text.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Text.swift new file mode 100644 index 00000000..79dfe2b2 --- /dev/null +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Text.swift @@ -0,0 +1,45 @@ +// +// UIImage+Text.swift +// LeadKit +// +// Created by Николай Ашанин on 28.09.16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit + +public extension UIImage { + + /** + creates a text label image. + + - parameter text: The text to use in the label. + - parameter font: The font (default: System font of size 18) + - parameter color: The text color (default: White) + - parameter backgroundColor: The background color (default:Gray). + - parameter size: Image size (default: 10x10) + - parameter offset: Center offset (default: 0x0) + */ + public convenience init?(text: String, + font: UIFont = UIFont.systemFontOfSize(18), + color: UIColor = UIColor.whiteColor(), + backgroundColor: UIColor = UIColor.grayColor(), + size: CGSize = CGSize(width: 10, height: 10), + offset: CGPoint = CGPoint(x: 0, y: 0)) { + let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height)) + label.font = font + label.text = text + label.textColor = color + label.textAlignment = .Center + label.backgroundColor = backgroundColor + let image = UIImage(fromView: label) + UIGraphicsBeginImageContextWithOptions(size, false, 0) + image?.drawInRect(CGRect(x: 0, y: 0, width: size.width, height: size.height)) + guard let cgImage = UIGraphicsGetImageFromCurrentImageContext().CGImage else { + return nil + } + self.init(CGImage: cgImage) + UIGraphicsEndImageContext() + } + +} diff --git a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Transformations.swift b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Transformations.swift index bad69574..e45964b6 100644 --- a/LeadKit/LeadKit/Extensions/UIImage/UIImage+Transformations.swift +++ b/LeadKit/LeadKit/Extensions/UIImage/UIImage+Transformations.swift @@ -32,4 +32,123 @@ public extension UIImage { return UIGraphicsGetImageFromCurrentImageContext() } + /** + creates a new image with rounded corners. + + - parameter cornerRadius: The corner radius. + + - returns: A new image + */ + public func roundCorners(cornerRadius: CGFloat) -> UIImage? { + guard let imageWithAlpha = applyAlpha() else { + return nil + } + UIGraphicsBeginImageContextWithOptions(size, false, 0) + let width = CGImageGetWidth(imageWithAlpha?.CGImage) + let height = CGImageGetHeight(imageWithAlpha?.CGImage) + let bits = CGImageGetBitsPerComponent(imageWithAlpha?.CGImage) + let colorSpace = CGImageGetColorSpace(imageWithAlpha?.CGImage) + let bitmapInfo = CGImageGetBitmapInfo(imageWithAlpha?.CGImage) + let context = CGBitmapContextCreate(nil, width, height, bits, 0, colorSpace, bitmapInfo.rawValue) + let rect = CGRect(x: 0, y: 0, width: CGFloat(width) * scale, height: CGFloat(height) * scale) + CGContextBeginPath(context) + if cornerRadius == 0 { + CGContextAddRect(context, rect) + } else { + CGContextSaveGState(context) + CGContextTranslateCTM(context, rect.minX, rect.minY) + CGContextScaleCTM(context, cornerRadius, cornerRadius) + let fw = rect.size.width / cornerRadius + let fh = rect.size.height / cornerRadius + CGContextMoveToPoint(context, fw, fh/2) + CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1) + CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1) + CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1) + CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1) + CGContextRestoreGState(context) + } + CGContextClosePath(context) + CGContextClip(context) + CGContextDrawImage(context, rect, imageWithAlpha?.CGImage) + guard let bitmapImage = CGBitmapContextCreateImage(context) else { + return nil + } + let image = UIImage(CGImage: bitmapImage, + scale: scale, + orientation: .Up) + UIGraphicsEndImageContext() + return image + } + + /** + creates a new image with rounded corners and border. + + - parameter cornerRadius: The corner radius. + - parameter border: The size of the border. + - parameter color: The color of the border. + + - returns: A new image + */ + public func roundCorners(cornerRadius: CGFloat, + border: CGFloat, + color: UIColor) -> UIImage? { + return roundCorners(cornerRadius)?.applyBorder(border, color: color) + } + + /** + creates a new circle image. + + - returns: A new image + */ + public func roundCornersToCircle() -> UIImage? { + let shortest = min(size.width, size.height) + return cropToSquare()?.roundCorners(shortest/2) + } + + /** + creates a new circle image with a border. + + - parameter border: CGFloat The size of the border. + - parameter color: UIColor The color of the border. + + - returns: UIImage? + */ + public func roundCornersToCircle(border border: CGFloat, color: UIColor) -> UIImage? { + let shortest = min(size.width, size.height) + return cropToSquare()?.roundCorners(shortest/2, border: border, color: color) + } + + /** + creates a new image with a border. + + - parameter border: The size of the border. + - parameter color: The color of the border. + + - returns: A new image + */ + public func applyBorder(border: CGFloat, + color: UIColor) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0) + let width = CGImageGetWidth(CGImage) + let height = CGImageGetHeight(CGImage) + let bits = CGImageGetBitsPerComponent(CGImage) + let colorSpace = CGImageGetColorSpace(CGImage) + let bitmapInfo = CGImageGetBitmapInfo(CGImage) + let context = CGBitmapContextCreate(nil, width, height, bits, 0, colorSpace, bitmapInfo.rawValue) + var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 + color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + CGContextSetRGBStrokeColor(context, red, green, blue, alpha) + CGContextSetLineWidth(context, border) + let rect = CGRect(x: 0, y: 0, width: size.width*scale, height: size.height*scale) + let inset = CGRectInset(rect, border*scale, border*scale) + CGContextStrokeEllipseInRect(context, inset) + CGContextDrawImage(context, inset, CGImage) + guard let bitmapImage = CGBitmapContextCreateImage(context) else { + return nil + } + let image = UIImage(CGImage: bitmapImage) + UIGraphicsEndImageContext() + return image + } + } diff --git a/LeadKit/LeadKit/Extensions/UIImageView/UIImageView+LoadingImage.swift b/LeadKit/LeadKit/Extensions/UIImageView/UIImageView+LoadingImage.swift new file mode 100644 index 00000000..ab4d9dbb --- /dev/null +++ b/LeadKit/LeadKit/Extensions/UIImageView/UIImageView+LoadingImage.swift @@ -0,0 +1,51 @@ +// +// UIImageView+LoadingImage.swift +// LeadKit +// +// Created by Николай Ашанин on 28.09.16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit +import QuartzCore + +public extension UIImageView { + + /** + loads an image from a URL. If cached, the cached image is returned. + otherwise, a place holder is used until the image from web is returned by the closure. + + - parameter url: The image URL. + - parameter placeholder: The placeholder image. + - parameter fadeIn: Weather the mage should fade in. + - parameter shouldCacheImage: Should be image cached. + - parameter closure: Returns the image from the web the first time is fetched. + + - returns: A new image + */ + public func imageFromURL(url: String, + placeholder: UIImage, + fadeIn: Bool = true, + shouldCacheImage: Bool = true, + closure: ((image: UIImage?) -> ())? = nil) { + + image = UIImage.imageFromURL(url, + placeholder: placeholder, + shouldCacheImage: shouldCacheImage) { (uploadedImage: UIImage?) in + + guard let image = uploadedImage else { + return nil + } + self.image = image + if fadeIn { + let transition = CATransition() + transition.duration = 0.5 + transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + transition.type = kCATransitionFade + layer.addAnimation(transition, forKey: nil) + } + closure?(image: image) + } + } + +}