463 lines
17 KiB
Swift
463 lines
17 KiB
Swift
//
|
||
// UIImage+Extension.swift
|
||
// GXM-CRM
|
||
//
|
||
// Created by XUXIAOTENG on 06/02/2018.
|
||
// Copyright © 2018 GuoXiaoMei. All rights reserved.
|
||
//
|
||
|
||
import Foundation
|
||
import UIKit
|
||
|
||
public extension UIImage {
|
||
|
||
func blend(with color: UIColor) -> UIImage? {
|
||
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
|
||
let context = UIGraphicsGetCurrentContext()!
|
||
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
||
// Unflip the image
|
||
context.translateBy(x: 0, y: size.height)
|
||
context.scaleBy(x: 1.0, y: -1.0)
|
||
|
||
context.setBlendMode(.overlay)
|
||
context.draw(cgImage!, in: rect)
|
||
|
||
context.setFillColor(color.cgColor)
|
||
context.fill(rect)
|
||
|
||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||
UIGraphicsEndImageContext()
|
||
return newImage
|
||
}
|
||
|
||
func blurImage(radius: CGFloat) -> UIImage {
|
||
let inputImage = CIImage(image: self)!
|
||
|
||
let parameters: [String: Any] = [
|
||
kCIInputRadiusKey: radius,
|
||
kCIInputImageKey: inputImage
|
||
]
|
||
let filter = CIFilter(name: "CIGaussianBlur",
|
||
parameters: parameters)!
|
||
|
||
let cgimg = CIContext().createCGImage(filter.outputImage!, from: inputImage.extent)
|
||
return UIImage(cgImage: cgimg!)
|
||
}
|
||
|
||
func saveToTemp(fileName: String) -> String? {
|
||
let dirURL = URL(fileURLWithPath: NSTemporaryDirectory())
|
||
let tempURL = dirURL.appendingPathComponent("GXM")
|
||
let fileURL = tempURL.appendingPathComponent(fileName).appendingPathExtension("jpg")
|
||
// 创建CRM目录
|
||
if !FileManager.default.fileExists(atPath: tempURL.path) {
|
||
do {
|
||
try FileManager.default.createDirectory(at: tempURL,
|
||
withIntermediateDirectories: false,
|
||
attributes: nil)
|
||
} catch {
|
||
print("目录创建失败: \(error)")
|
||
ProgressHUD.showError(withStatus: error.localizedDescription)
|
||
}
|
||
}
|
||
if FileManager.default.fileExists(atPath: fileURL.path) {
|
||
do {
|
||
try FileManager.default.removeItem(at: fileURL)
|
||
} catch {
|
||
print("文件删除失败:\(error)")
|
||
ProgressHUD.showError(withStatus: error.localizedDescription)
|
||
}
|
||
}
|
||
if let data = self.jpegData(compressionQuality: 1.0) {
|
||
do {
|
||
try data.write(to: fileURL)
|
||
return fileURL.path
|
||
} catch {
|
||
print("文件保存失败:\(error)")
|
||
ProgressHUD.showError(withStatus: error.localizedDescription)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Thumbnail image
|
||
func thumbnail(size: CGFloat) -> UIImage {
|
||
let scale = UIScreen.main.scale
|
||
let length = size * scale
|
||
let thumbnail = resizeTo(CGSize(width: length, height: length), scale: false) ?? self
|
||
return thumbnail
|
||
}
|
||
|
||
func resizeTo(_ size: CGSize) -> UIImage? {
|
||
guard let imgRef = cgImage else { return self }
|
||
let srcSize = CGSize(width: imgRef.width, height: imgRef.height)
|
||
var dstSize = size
|
||
|
||
// Don't resize
|
||
guard !(srcSize.equalTo(dstSize)) else { return self }
|
||
|
||
let scaleRatio: CGFloat = dstSize.width / srcSize.width
|
||
let orient = imageOrientation
|
||
var transform = CGAffineTransform.identity
|
||
|
||
switch orient {
|
||
case .up: //EXIF = 1
|
||
transform = CGAffineTransform.identity
|
||
|
||
case .upMirrored: //EXIF = 2
|
||
transform = CGAffineTransform(translationX: srcSize.width, y: 0)
|
||
transform = transform.scaledBy(x: -1.0, y: 1.0)
|
||
|
||
case .down: //EXIF = 3
|
||
transform = CGAffineTransform(translationX: srcSize.width, y: srcSize.height)
|
||
transform = transform.rotated(by: CGFloat.pi)
|
||
|
||
case .downMirrored: //EXIF = 4
|
||
transform = CGAffineTransform(translationX: 0, y: srcSize.height)
|
||
transform = transform.scaledBy(x: 1.0, y: -1.0)
|
||
|
||
case .leftMirrored: //EXIF = 5
|
||
dstSize = CGSize(width: dstSize.height, height: dstSize.width)
|
||
transform = CGAffineTransform(translationX: srcSize.height, y: srcSize.width)
|
||
transform = transform.scaledBy(x: -1.0, y: 1.0)
|
||
transform = transform.rotated(by: 3.0 * CGFloat.pi / 2.0)
|
||
|
||
case .left: //EXIF = 6
|
||
dstSize = CGSize(width: dstSize.height, height: dstSize.width)
|
||
transform = CGAffineTransform(translationX: 0.0, y: srcSize.width)
|
||
transform = transform.rotated(by: 3.0 * CGFloat.pi / 2.0)
|
||
|
||
case .rightMirrored: //EXIF = 7
|
||
dstSize = CGSize(width: dstSize.height, height: dstSize.width)
|
||
transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
|
||
transform = transform.rotated(by: CGFloat.pi / 2.0)
|
||
|
||
case .right: //EXIF = 8
|
||
dstSize = CGSize(width: dstSize.height, height: dstSize.width)
|
||
transform = CGAffineTransform(translationX: srcSize.height, y: 0)
|
||
transform = transform.rotated(by: CGFloat.pi / 2.0)
|
||
@unknown default:
|
||
break
|
||
}
|
||
|
||
// Draw the image on a new context, applying a transform matrix
|
||
UIGraphicsBeginImageContextWithOptions(dstSize, false, scale)
|
||
|
||
guard let context = UIGraphicsGetCurrentContext() else {
|
||
return nil
|
||
}
|
||
|
||
if orient == .right || orient == .left {
|
||
context.scaleBy(x: -scaleRatio, y: scaleRatio)
|
||
context.translateBy(x: -srcSize.height, y: 0)
|
||
} else {
|
||
context.scaleBy(x: scaleRatio, y: -scaleRatio)
|
||
context.translateBy(x: 0, y: -srcSize.height)
|
||
}
|
||
context.concatenate(transform)
|
||
|
||
let rect = CGRect(x: 0, y: 0, width: srcSize.width, height: srcSize.height)
|
||
context.draw(imgRef, in: rect)
|
||
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
|
||
UIGraphicsEndImageContext()
|
||
|
||
return resizedImage
|
||
}
|
||
|
||
func resizeTo(_ size: CGSize, scale: Bool) -> UIImage? {
|
||
guard let imgRef = cgImage else { return self }
|
||
let srcSize = CGSize(width: imgRef.width, height: imgRef.height)
|
||
var boundingSize = size
|
||
|
||
let orient = self.imageOrientation
|
||
switch orient {
|
||
case .left,
|
||
.right,
|
||
.leftMirrored,
|
||
.rightMirrored:
|
||
boundingSize = CGSize(width: boundingSize.height, height: boundingSize.width)
|
||
default:
|
||
// NOP
|
||
break
|
||
}
|
||
|
||
// Compute the target CGRect
|
||
var dstSize: CGSize
|
||
|
||
if !scale && srcSize.width < boundingSize.width && srcSize.height < boundingSize.height {
|
||
// no resize (draw the image anyway to take image orientation into account)
|
||
dstSize = srcSize
|
||
} else {
|
||
let wRatio = boundingSize.width / srcSize.width
|
||
let hRatio = boundingSize.height / srcSize.height
|
||
|
||
if wRatio < hRatio {
|
||
dstSize = CGSize(width: boundingSize.width, height: CGFloat(floorf(Float(srcSize.height * wRatio))))
|
||
} else {
|
||
dstSize = CGSize(width: CGFloat(floorf(Float(srcSize.width * hRatio))), height: boundingSize.height)
|
||
}
|
||
}
|
||
|
||
return resizeTo(dstSize)
|
||
}
|
||
|
||
}
|
||
|
||
public extension UIImage {
|
||
|
||
func scaledWidth(for height: CGFloat) -> CGFloat {
|
||
return height * size.width / size.height
|
||
}
|
||
|
||
func scaledHeight(for width: CGFloat) -> CGFloat {
|
||
return width * size.height / size.width
|
||
}
|
||
}
|
||
|
||
public extension UIImage {
|
||
/// 更改图片颜色/大小
|
||
/// - Parameters:
|
||
/// - tintColor: 颜色
|
||
/// - scaleSize: 缩放size
|
||
/// - blendMode: 默认destinationIn
|
||
/// - Returns: 图片
|
||
func image(tintColor: UIColor,
|
||
scaleSize : CGSize,
|
||
blendMode: CGBlendMode = .destinationIn) -> UIImage? {
|
||
let tempSize = scaleSize == .zero ? size : scaleSize
|
||
UIGraphicsBeginImageContextWithOptions(tempSize, false, 0.0)
|
||
// 更改图片大小
|
||
let bounds = CGRect(x: 0, y: 0, width: tempSize.width, height: tempSize.height)
|
||
// 更改图片颜色
|
||
tintColor.setFill()
|
||
UIRectFill(bounds)
|
||
draw(in: bounds, blendMode: blendMode, alpha: 1.0)
|
||
guard let tintImage = UIGraphicsGetImageFromCurrentImageContext() else {
|
||
return self
|
||
}
|
||
UIGraphicsEndImageContext()
|
||
return tintImage
|
||
}
|
||
|
||
/// 更改图片颜色
|
||
/// - Parameters:
|
||
/// - tintColor: 颜色
|
||
/// - blendMode: 默认destinationIn
|
||
/// - Returns: 图片
|
||
func image(tintColor: UIColor,
|
||
blendMode: CGBlendMode = .destinationIn) -> UIImage? {
|
||
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||
let bounds = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
||
// 更改图片颜色
|
||
tintColor.setFill()
|
||
UIRectFill(bounds)
|
||
draw(in: bounds, blendMode: blendMode, alpha: 1.0)
|
||
guard let tintImage = UIGraphicsGetImageFromCurrentImageContext() else {
|
||
return self
|
||
}
|
||
UIGraphicsEndImageContext()
|
||
return tintImage
|
||
}
|
||
|
||
/// 更改图片大小
|
||
/// - Parameters:
|
||
/// - scaleSize: 缩放size
|
||
/// - blendMode: 默认destinationIn
|
||
/// - Returns: 图片
|
||
func image(scaleSize : CGSize,
|
||
blendMode: CGBlendMode = .destinationIn) -> UIImage? {
|
||
let tempSize = scaleSize == .zero ? size : scaleSize
|
||
UIGraphicsBeginImageContextWithOptions(tempSize, false, 0.0)
|
||
// 更改图片大小
|
||
let bounds = CGRect(x: 0, y: 0, width: tempSize.width, height: tempSize.height)
|
||
UIRectFill(bounds)
|
||
draw(in: bounds, blendMode: blendMode, alpha: 1.0)
|
||
guard let tintImage = UIGraphicsGetImageFromCurrentImageContext() else {
|
||
return self
|
||
}
|
||
UIGraphicsEndImageContext()
|
||
return tintImage
|
||
}
|
||
|
||
/// 生成颜色图片
|
||
/// - Parameter color: 颜色
|
||
/// - Returns: UIImage
|
||
static func colorImage(with color: UIColor) -> UIImage? {
|
||
var image: UIImage?
|
||
let rect = CGRect(origin: .zero, size: CGSize(width: 1, height: 1))
|
||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
|
||
if let context = UIGraphicsGetCurrentContext() {
|
||
context.setFillColor(color.cgColor)
|
||
context.fill(rect)
|
||
image = UIGraphicsGetImageFromCurrentImageContext()
|
||
}
|
||
UIGraphicsEndImageContext()
|
||
return image
|
||
}
|
||
}
|
||
|
||
// MARK: - 旋转图片
|
||
extension UIImage {
|
||
/// 图片镜像处理
|
||
public var adaptiveImage: UIImage? {
|
||
guard let cgImage = cgImage else { return nil }
|
||
let flippedImage = UIImage(cgImage: cgImage, scale: scale, orientation: UIView.appearance().semanticContentAttribute == .forceRightToLeft ? .upMirrored : .up)
|
||
return flippedImage
|
||
}
|
||
}
|
||
|
||
// MARK: - 压缩图片
|
||
extension UIImage {
|
||
public func compressImage(maxSize: CGFloat) -> Data? {
|
||
var imageData = self.jpegData(compressionQuality: 1.0)
|
||
if imageData?.count ?? 0 > Int(maxSize) {
|
||
var compressionQuality = 1.0
|
||
let sizeRatio = self.size.width / self.size.height
|
||
|
||
var resizedImage = resizeImage(image: self, targetSize: CGSize(width: 360, height: 360 / sizeRatio))
|
||
imageData = resizedImage?.jpegData(compressionQuality: compressionQuality)
|
||
|
||
while imageData?.count ?? 0 > Int(maxSize) {
|
||
compressionQuality -= 0.1
|
||
// resizedImage = resizeImage(image: resizedImage!, targetSize: CGSize(width: resizedImage!.size.width,
|
||
// height: resizedImage!.size.height))
|
||
imageData = resizedImage?.jpegData(compressionQuality: compressionQuality)
|
||
}
|
||
}
|
||
return imageData
|
||
}
|
||
|
||
private func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage? {
|
||
let size = image.size
|
||
|
||
// let widthRatio = targetSize.width / size.width
|
||
// let heightRatio = targetSize.height / size.height
|
||
//
|
||
// let newSize: CGSize
|
||
// if widthRatio > heightRatio {
|
||
// newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
|
||
// } else {
|
||
// newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
|
||
// }
|
||
|
||
UIGraphicsBeginImageContextWithOptions(targetSize, false, 0.0)
|
||
image.draw(in: CGRect(origin: CGPoint.zero, size: targetSize))
|
||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||
UIGraphicsEndImageContext()
|
||
|
||
return newImage
|
||
}
|
||
}
|
||
|
||
// 修复图片旋转
|
||
extension UIImage {
|
||
public func fixOrientation() -> UIImage {
|
||
if self.imageOrientation == .up {
|
||
return self
|
||
}
|
||
|
||
var transform = CGAffineTransform.identity
|
||
|
||
switch self.imageOrientation {
|
||
case .down, .downMirrored:
|
||
transform = transform.translatedBy(x: self.size.width, y: self.size.height)
|
||
transform = transform.rotated(by: .pi)
|
||
break
|
||
|
||
case .left, .leftMirrored:
|
||
transform = transform.translatedBy(x: self.size.width, y: 0)
|
||
transform = transform.rotated(by: .pi / 2)
|
||
break
|
||
|
||
case .right, .rightMirrored:
|
||
transform = transform.translatedBy(x: 0, y: self.size.height)
|
||
transform = transform.rotated(by: -.pi / 2)
|
||
break
|
||
|
||
default:
|
||
break
|
||
}
|
||
|
||
switch self.imageOrientation {
|
||
case .upMirrored, .downMirrored:
|
||
transform = transform.translatedBy(x: self.size.width, y: 0)
|
||
transform = transform.scaledBy(x: -1, y: 1)
|
||
break
|
||
|
||
case .leftMirrored, .rightMirrored:
|
||
transform = transform.translatedBy(x: self.size.height, y: 0);
|
||
transform = transform.scaledBy(x: -1, y: 1)
|
||
break
|
||
|
||
default:
|
||
break
|
||
}
|
||
|
||
let ctx = CGContext(data: nil, width: Int(self.size.width), height: Int(self.size.height), bitsPerComponent: self.cgImage!.bitsPerComponent, bytesPerRow: 0, space: self.cgImage!.colorSpace!, bitmapInfo: self.cgImage!.bitmapInfo.rawValue)
|
||
ctx?.concatenate(transform)
|
||
|
||
switch self.imageOrientation {
|
||
case .left, .leftMirrored, .right, .rightMirrored:
|
||
ctx?.draw(self.cgImage!, in: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(size.height), height: CGFloat(size.width)))
|
||
break
|
||
|
||
default:
|
||
ctx?.draw(self.cgImage!, in: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(size.width), height: CGFloat(size.height)))
|
||
break
|
||
}
|
||
|
||
let cgimg: CGImage = (ctx?.makeImage())!
|
||
let img = UIImage(cgImage: cgimg)
|
||
|
||
return img
|
||
}
|
||
}
|
||
|
||
// UIImage扩展:支持从Data创建GIF动画
|
||
extension UIImage {
|
||
class func gifImageWithData(_ data: Data) -> UIImage? {
|
||
// 创建图片源
|
||
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
|
||
return nil
|
||
}
|
||
|
||
let count = CGImageSourceGetCount(source)
|
||
var images = [UIImage]()
|
||
var duration: TimeInterval = 0
|
||
|
||
// 遍历所有帧
|
||
for i in 0..<count {
|
||
// 获取单帧图片
|
||
guard let cgImage = CGImageSourceCreateImageAtIndex(source, i, nil) else {
|
||
continue
|
||
}
|
||
let image = UIImage(cgImage: cgImage)
|
||
images.append(image)
|
||
|
||
// 累加每帧的持续时间
|
||
duration += UIImage.gifFrameDuration(source: source, index: i)
|
||
}
|
||
|
||
// 创建动画图片
|
||
return UIImage.animatedImage(with: images, duration: duration)
|
||
}
|
||
|
||
// 获取单帧的持续时间
|
||
private class func gifFrameDuration(source: CGImageSource, index: Int) -> TimeInterval {
|
||
let defaultDuration = 0.1
|
||
guard let properties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as? [AnyHashable: Any] else {
|
||
return defaultDuration
|
||
}
|
||
|
||
guard let gifProperties = properties[kCGImagePropertyGIFDictionary as String] as? [AnyHashable: Any] else {
|
||
return defaultDuration
|
||
}
|
||
|
||
// 处理GIF的延迟时间
|
||
if let delayTime = gifProperties[kCGImagePropertyGIFDelayTime as String] as? TimeInterval {
|
||
return delayTime > 0 ? delayTime : defaultDuration
|
||
}
|
||
|
||
return defaultDuration
|
||
}
|
||
}
|