image rotation
This commit is contained in:
parent
99ef62dfea
commit
38eb97d60f
|
|
@ -274,6 +274,14 @@
|
|||
671463CF1EB34B1E00EAB194 /* TestView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671463B71EB34B1E00EAB194 /* TestView.xib */; };
|
||||
67186B311EB248F100CFAFFB /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67186B281EB248F100CFAFFB /* LeadKit.framework */; };
|
||||
67186B3F1EB24A1900CFAFFB /* LeadKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 67186B201EB247A200CFAFFB /* LeadKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
672CC2A81FEA6A6A00EBFB0A /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2A71FEA6A6A00EBFB0A /* RotateDrawingOperation.swift */; };
|
||||
672CC2A91FEA6A7400EBFB0A /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2A71FEA6A6A00EBFB0A /* RotateDrawingOperation.swift */; };
|
||||
672CC2AA1FEA6A7500EBFB0A /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2A71FEA6A6A00EBFB0A /* RotateDrawingOperation.swift */; };
|
||||
672CC2AB1FEA6A7600EBFB0A /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2A71FEA6A6A00EBFB0A /* RotateDrawingOperation.swift */; };
|
||||
672CC2B31FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2B21FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift */; };
|
||||
672CC2B41FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2B21FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift */; };
|
||||
672CC2B51FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2B21FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift */; };
|
||||
672CC2B61FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672CC2B21FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift */; };
|
||||
6740D5D21FABDA46006BB7C0 /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6740D5D11FABDA46006BB7C0 /* DataSourceProtocol.swift */; };
|
||||
6740D5D31FABDA46006BB7C0 /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6740D5D11FABDA46006BB7C0 /* DataSourceProtocol.swift */; };
|
||||
6740D5D41FABDA46006BB7C0 /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6740D5D11FABDA46006BB7C0 /* DataSourceProtocol.swift */; };
|
||||
|
|
@ -558,6 +566,8 @@
|
|||
67186B301EB248F100CFAFFB /* LeadKit iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LeadKit iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
67186B411EB24AA000CFAFFB /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
67186C1A1EB24B7800CFAFFB /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
|
||||
672CC2A71FEA6A6A00EBFB0A /* RotateDrawingOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotateDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
672CC2B21FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FloatingPoint+DegreesRadiansConvertion.swift"; sourceTree = "<group>"; };
|
||||
6740D5D11FABDA46006BB7C0 /* DataSourceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourceProtocol.swift; sourceTree = "<group>"; };
|
||||
674AF55B1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActivityIndicatorView+LoadingIndicator.swift"; sourceTree = "<group>"; };
|
||||
6771DFD71EE99EBA002DCDAE /* DateFormattingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormattingService.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -776,6 +786,7 @@
|
|||
671461DA1EB3396E00EAB194 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
672CC2B11FEA727D00EBFB0A /* FloatingPoint */,
|
||||
678B43F41FBA3D7C00D1F77D /* Views */,
|
||||
67CB1BF81FAB793F0089D1B1 /* Pagination */,
|
||||
67F139FF1FAB4FCC008175B4 /* Rx */,
|
||||
|
|
@ -1028,6 +1039,7 @@
|
|||
6714623B1EB3396E00EAB194 /* ImageDrawingOperation.swift */,
|
||||
6714623C1EB3396E00EAB194 /* PaddingDrawingOperation.swift */,
|
||||
6714623D1EB3396E00EAB194 /* ResizeDrawingOperation.swift */,
|
||||
672CC2A71FEA6A6A00EBFB0A /* RotateDrawingOperation.swift */,
|
||||
6714623E1EB3396E00EAB194 /* RoundDrawingOperation.swift */,
|
||||
6714623F1EB3396E00EAB194 /* SolidFillDrawingOperation.swift */,
|
||||
671462401EB3396E00EAB194 /* TemplateDrawingOperation.swift */,
|
||||
|
|
@ -1112,6 +1124,14 @@
|
|||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
672CC2B11FEA727D00EBFB0A /* FloatingPoint */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
672CC2B21FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift */,
|
||||
);
|
||||
path = FloatingPoint;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
674AF55A1EC45B1600038A8F /* UIActivityIndicatorView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -2184,6 +2204,7 @@
|
|||
6714639E1EB33AEB00EAB194 /* NetworkService+ActivityIndicator.swift in Sources */,
|
||||
A6E0DDEF1F8A6C57002CA74E /* CellSeparatorType.swift in Sources */,
|
||||
6714634C1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */,
|
||||
672CC2A81FEA6A6A00EBFB0A /* RotateDrawingOperation.swift in Sources */,
|
||||
671462F01EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */,
|
||||
6771DFDE1EE99F6F002DCDAE /* DateFormattingArguments.swift in Sources */,
|
||||
671462681EB3396E00EAB194 /* NetworkService.swift in Sources */,
|
||||
|
|
@ -2195,6 +2216,7 @@
|
|||
67F13A431FAB6256008175B4 /* PaginationLoadingViewModel.swift in Sources */,
|
||||
67A1FF8F1EBCA09B00D6C89F /* UIImage+Spinner.swift in Sources */,
|
||||
67CB1BFE1FAB79EE0089D1B1 /* UICollectionView+PaginationWrappable.swift in Sources */,
|
||||
672CC2B31FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */,
|
||||
671462901EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
|
||||
671462FC1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */,
|
||||
671463841EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
|
||||
|
|
@ -2321,6 +2343,7 @@
|
|||
A676AE4D1F9810C1001F9214 /* Any+Cast.swift in Sources */,
|
||||
67F13A3B1FAB60DE008175B4 /* GeneralLoadingViewModelConfiguration.swift in Sources */,
|
||||
EFBE57D21EC35EF20040E00A /* Array+Extensions.swift in Sources */,
|
||||
672CC2A91FEA6A7400EBFB0A /* RotateDrawingOperation.swift in Sources */,
|
||||
67F139F71FAB4F22008175B4 /* TotalCountCursor.swift in Sources */,
|
||||
671462821EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
|
||||
671463561EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
|
||||
|
|
@ -2333,6 +2356,7 @@
|
|||
6714634E1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */,
|
||||
6714626A1EB3396E00EAB194 /* NetworkService.swift in Sources */,
|
||||
671463421EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
|
||||
672CC2B51FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */,
|
||||
67F13A171FAB5A87008175B4 /* LoadingState.swift in Sources */,
|
||||
671462921EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
|
||||
671463861EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
|
||||
|
|
@ -2399,6 +2423,7 @@
|
|||
671462831EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
|
||||
671463571EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
|
||||
67F13A371FAB60C0008175B4 /* GeneralLoadingViewModel.swift in Sources */,
|
||||
672CC2B61FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */,
|
||||
67F139FE1FAB4F7E008175B4 /* TotalCountCursorConfiguration.swift in Sources */,
|
||||
671463631EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
|
||||
671462871EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
|
||||
|
|
@ -2454,6 +2479,7 @@
|
|||
6714625F1EB3396E00EAB194 /* LogFormatter.swift in Sources */,
|
||||
6714630B1EB3396E00EAB194 /* UIView+Rotation.swift in Sources */,
|
||||
6714626F1EB3396E00EAB194 /* XibView.swift in Sources */,
|
||||
672CC2AA1FEA6A7500EBFB0A /* RotateDrawingOperation.swift in Sources */,
|
||||
6714637F1EB3396E00EAB194 /* ImageDrawingOperation.swift in Sources */,
|
||||
671463371EB3396E00EAB194 /* DrawingOperation.swift in Sources */,
|
||||
67F13A221FAB5AE8008175B4 /* LoadingConfiguration.swift in Sources */,
|
||||
|
|
@ -2521,6 +2547,7 @@
|
|||
6771DFEB1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */,
|
||||
67F13A3F1FAB614D008175B4 /* PaginationLoadingViewModelConfiguration.swift in Sources */,
|
||||
67A1FF951EBCA65E00D6C89F /* CABasicAnimation+Rotation.swift in Sources */,
|
||||
672CC2AB1FEA6A7600EBFB0A /* RotateDrawingOperation.swift in Sources */,
|
||||
671462811EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
|
||||
671463551EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
|
||||
671463611EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
|
||||
|
|
@ -2536,6 +2563,7 @@
|
|||
67051ADC1EBC7C36008EADC0 /* SpinnerView.swift in Sources */,
|
||||
671462FD1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */,
|
||||
671463851EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
|
||||
672CC2B41FEA72A000EBFB0A /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */,
|
||||
671462D11EB3396E00EAB194 /* UIScrollView+Support.swift in Sources */,
|
||||
671463911EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */,
|
||||
67F13A201FAB5AE8008175B4 /* LoadingConfiguration.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public extension FloatingPoint {
|
||||
|
||||
/// Converts degrees to radians
|
||||
///
|
||||
/// - Returns: radians
|
||||
func degreesToRadians() -> Self {
|
||||
return self * .pi / 180
|
||||
}
|
||||
|
||||
/// Converts radians to degrees
|
||||
///
|
||||
/// - Returns: degrees
|
||||
func radiansToDegrees() -> Self {
|
||||
return self * 180 / .pi
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public extension UIImage {
|
|||
/// Creates an image from a UIView.
|
||||
///
|
||||
/// - Parameter fromView: The source view.
|
||||
/// - Returns: A new instance of UIImage or nil if something goes wrong.
|
||||
/// - Returns: A new instance of UIImage.
|
||||
static func imageFrom(view: UIView) -> UIImage {
|
||||
let operation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
|
||||
|
|
@ -54,17 +54,15 @@ public extension UIImage {
|
|||
/// Render current template UIImage into new image using given color.
|
||||
///
|
||||
/// - Parameter color: Color to fill template image.
|
||||
/// - Returns: A new UIImage rendered with given color.
|
||||
/// - Returns: A new UIImage rendered with given color or original image if something goes wrong.
|
||||
func renderTemplate(withColor color: UIColor) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a new image with rounded corners and border.
|
||||
|
|
@ -74,49 +72,45 @@ public extension UIImage {
|
|||
/// - borderWidth: The size of the border.
|
||||
/// - color: The color of the border.
|
||||
/// - extendSize: Extend result image size and don't overlap source image by border.
|
||||
/// - Returns: A new image with rounded corners.
|
||||
/// - Returns: A new image with rounded corners or original image if something goes wrong.
|
||||
func roundCorners(cornerRadius: CGFloat,
|
||||
borderWidth: CGFloat,
|
||||
color: UIColor,
|
||||
extendSize: Bool = false) -> UIImage {
|
||||
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: cornerRadius)
|
||||
|
||||
guard let roundImage = roundOperation.imageFromNewRenderer(scale: scale).cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: size,
|
||||
border: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: cornerRadius)
|
||||
|
||||
guard let roundImage = roundOperation.imageFromNewRenderer(scale: scale).cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: size,
|
||||
border: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - Returns: A new circled image.
|
||||
/// - Returns: A new circled image or original image if something goes wrong.
|
||||
func roundCornersToCircle() -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Creates a new circle image with a border.
|
||||
|
|
@ -125,33 +119,31 @@ public extension UIImage {
|
|||
/// - borderWidth: The size of the border.
|
||||
/// - borderColor: The color of the border.
|
||||
/// - extendSize: Extend result image size and don't overlap source image by border (default = false).
|
||||
/// - Returns: A new image with rounded corners or nil if something goes wrong.
|
||||
/// - Returns: A new image with rounded corners or original image if something goes wrong.
|
||||
func roundCornersToCircle(borderWidth: CGFloat,
|
||||
borderColor: UIColor,
|
||||
extendSize: Bool = false) -> UIImage {
|
||||
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
guard let roundImage = roundOperation.imageFromNewRenderer(scale: scale).cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: size,
|
||||
border: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
guard let roundImage = roundOperation.imageFromNewRenderer(scale: scale).cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: size,
|
||||
border: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of an image.
|
||||
|
|
@ -161,62 +153,85 @@ public extension UIImage {
|
|||
/// - contentMode: The way to handle the content in the new size.
|
||||
/// - cropToImageBounds: Should output image size match resized image size.
|
||||
/// Note: If passed true with ResizeMode.scaleAspectFit content mode it will give the original image.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
/// - Returns: A new image scaled to new size or original image if something goes wrong.
|
||||
func resize(newSize: CGSize,
|
||||
contentMode: ResizeMode = .scaleToFill,
|
||||
cropToImageBounds: Bool = false) -> UIImage {
|
||||
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Adds an alpha channel if UIImage doesn't already have one.
|
||||
///
|
||||
/// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one.
|
||||
func applyAlpha() -> UIImage {
|
||||
guard let image = cgImage, !image.hasAlpha else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
guard !image.hasAlpha else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
///
|
||||
/// - Parameter padding: The padding amount.
|
||||
/// - Returns: A new padded image or nil if something goes wrong.
|
||||
/// - Returns: A new padded image or original image if something goes wrong.
|
||||
func applyPadding(_ padding: CGFloat) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
return withCGImage { image in
|
||||
let operation = PaddingDrawingOperation(image: image, imageSize: size, padding: padding)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
}
|
||||
|
||||
let operation = PaddingDrawingOperation(image: image, imageSize: size, padding: padding)
|
||||
/// Creates a copy of the image rotated by the given amount of degrees.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - degrees: The number of degrees.
|
||||
/// - clockwise: Should rotate image clockwise.
|
||||
/// - Returns: A new rotated image or original image if something goes wrong.
|
||||
func rotate(degrees: CGFloat, clockwise: Bool = true) -> UIImage {
|
||||
return withCGImage { image in
|
||||
let radians = degrees.degreesToRadians()
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
let operation = RotateDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radians: radians,
|
||||
clockwise: clockwise)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
}
|
||||
|
||||
/// Workaround to fix flipped image rendering (by Y)
|
||||
private func redraw() -> UIImage {
|
||||
return withCGImage { image in
|
||||
let operation = ImageDrawingOperation(image: image, newSize: size)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
}
|
||||
|
||||
private func withCGImage(_ actionClosure: (CGImage) -> UIImage) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image, newSize: size)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
return actionClosure(image)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,9 +44,15 @@ public extension Support where Base: UIImage {
|
|||
/// - Parameter fromView: The source view.
|
||||
/// - Returns: A new instance of UIImage or nil if something goes wrong.
|
||||
static func imageFrom(view: UIView) -> Support<UIImage>? {
|
||||
let layerDrawingOperation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
let operation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
|
||||
return layerDrawingOperation.imageFromNewContext(scale: UIScreen.main.scale)?.support.flipY()
|
||||
guard let rotatedImage = operation.imageFromNewContext(scale: UIScreen.main.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let flipOperation = rotatedImage.cgImage?.flipYOperation(size: rotatedImage.size)
|
||||
|
||||
return flipOperation?.imageFromNewContext(scale: rotatedImage.scale)?.support
|
||||
}
|
||||
|
||||
/// Render current template UIImage into new image using given color.
|
||||
|
|
@ -54,15 +60,19 @@ public extension Support where Base: UIImage {
|
|||
/// - Parameter color: Color to fill template image.
|
||||
/// - Returns: A new UIImage rendered with given color or nil if something goes wrong.
|
||||
func renderTemplate(withColor color: UIColor) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
color: color.cgColor)
|
||||
|
||||
guard let templateImage = operation.imageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let flipOperation = templateImage.cgImage?.flipYOperation(size: templateImage.size)
|
||||
|
||||
return flipOperation?.imageFromNewContext(scale: templateImage.scale)
|
||||
}
|
||||
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support.flipY()
|
||||
}
|
||||
|
||||
/// Creates a new image with rounded corners and border.
|
||||
|
|
@ -78,43 +88,39 @@ public extension Support where Base: UIImage {
|
|||
color: UIColor,
|
||||
extendSize: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: cornerRadius)
|
||||
|
||||
guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: base.size,
|
||||
border: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: cornerRadius)
|
||||
|
||||
guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: base.size,
|
||||
border: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - Returns: A new circled image or nil if something goes wrong.
|
||||
func roundCornersToCircle() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a new circle image with a border.
|
||||
|
|
@ -128,28 +134,26 @@ public extension Support where Base: UIImage {
|
|||
borderColor: UIColor,
|
||||
extendSize: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: base.size,
|
||||
border: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: base.size,
|
||||
border: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a resized copy of an image.
|
||||
|
|
@ -164,32 +168,28 @@ public extension Support where Base: UIImage {
|
|||
contentMode: ResizeMode = .scaleToFill,
|
||||
cropToImageBounds: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Adds an alpha channel if UIImage doesn't already have one.
|
||||
///
|
||||
/// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one.
|
||||
func applyAlpha() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage, !image.hasAlpha else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
|
|
@ -197,29 +197,58 @@ public extension Support where Base: UIImage {
|
|||
/// - Parameter padding: The padding amount.
|
||||
/// - Returns: A new padded image or nil if something goes wrong.
|
||||
func applyPadding(_ padding: CGFloat) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
return withCGImage { image in
|
||||
let operation = PaddingDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
padding: padding)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
let operation = PaddingDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
padding: padding)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
private func flipY() -> Support<UIImage>? {
|
||||
/// Creates a copy of the image rotated by the given amount of degrees.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - degrees: The number of degrees.
|
||||
/// - clockwise: Should rotate image clockwise.
|
||||
/// - Returns: A new rotated image or nil if something goes wrong.
|
||||
func rotate(degrees: CGFloat, clockwise: Bool = true) -> Support<UIImage>? {
|
||||
return withCGImage { image in
|
||||
let radians = degrees.degreesToRadians()
|
||||
|
||||
let operation = RotateDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radians: radians,
|
||||
clockwise: clockwise)
|
||||
|
||||
guard let rotatedImage = operation.imageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let flipOperation = rotatedImage.cgImage?.flipYOperation(size: rotatedImage.size)
|
||||
|
||||
return flipOperation?.imageFromNewContext(scale: rotatedImage.scale)
|
||||
}
|
||||
}
|
||||
|
||||
private func withCGImage(_ actionClosure: (CGImage) -> UIImage?) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let flipOperation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
origin: .zero,
|
||||
opaque: false,
|
||||
flipY: true)
|
||||
return actionClosure(image)?.support
|
||||
}
|
||||
|
||||
return flipOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
private extension CGImage {
|
||||
|
||||
func flipYOperation(size: CGSize) -> ImageDrawingOperation {
|
||||
return ImageDrawingOperation(image: self,
|
||||
newSize: size,
|
||||
origin: .zero,
|
||||
opaque: false,
|
||||
flipY: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct RotateDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let imageSize: CGSize
|
||||
private let radians: CGFloat
|
||||
|
||||
private let translateRect: CGRect
|
||||
|
||||
public init(image: CGImage, imageSize: CGSize, radians: CGFloat, clockwise: Bool = true) {
|
||||
self.image = image
|
||||
self.imageSize = imageSize
|
||||
self.radians = clockwise ? radians : -radians
|
||||
|
||||
let transform = CGAffineTransform(rotationAngle: radians)
|
||||
let imageRect = CGRect(origin: .zero, size: imageSize)
|
||||
|
||||
translateRect = CGRect(origin: .zero, size: imageRect.applying(transform).size)
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return translateRect.size.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.translateBy(x: translateRect.midX, y: translateRect.midY)
|
||||
context.rotate(by: radians)
|
||||
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
|
||||
let imageLocation = CGRect(origin: CGPoint(x: -imageSize.width / 2, y: -imageSize.height / 2),
|
||||
size: imageSize)
|
||||
|
||||
context.draw(image, in: imageLocation)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue