More details
- Xcode 10.2.1 (10E1001), Swift 5
Solution 1
guard let image = UIImage(named: "img") else { return } let jpegData = image.jpegData(compressionQuality: 1.0) let pngData = image.pngData()
Solution 2.1
extension UIImage { func toData (options: NSDictionary, type: CFString) -> Data? { guard let cgImage = cgImage else { return nil } return autoreleasepool { () -> Data? in let data = NSMutableData() guard let imageDestination = CGImageDestinationCreateWithData(data as CFMutableData, type, 1, nil) else { return nil } CGImageDestinationAddImage(imageDestination, cgImage, options) CGImageDestinationFinalize(imageDestination) return data as Data } } }
Using Solution 2.1
// about properties: https://developer.apple.com/documentation/imageio/1464962-cgimagedestinationaddimage let options: NSDictionary = [ kCGImagePropertyOrientation: 6, kCGImagePropertyHasAlpha: true, kCGImageDestinationLossyCompressionQuality: 0.5 ] // https://developer.apple.com/documentation/mobilecoreservices/uttype/uti_image_content_types guard let data = image.toData(options: options, type: kUTTypeJPEG) else { return } let size = CGFloat(data.count)/1000.0/1024.0 print("\(size) mb")
Solution 2.2
extension UIImage { func toJpegData (compressionQuality: CGFloat, hasAlpha: Bool = true, orientation: Int = 6) -> Data? { guard cgImage != nil else { return nil } let options: NSDictionary = [ kCGImagePropertyOrientation: orientation, kCGImagePropertyHasAlpha: hasAlpha, kCGImageDestinationLossyCompressionQuality: compressionQuality ] return toData(options: options, type: .jpeg) } func toData (options: NSDictionary, type: ImageType) -> Data? { guard cgImage != nil else { return nil } return toData(options: options, type: type.value) } // about properties: https://developer.apple.com/documentation/imageio/1464962-cgimagedestinationaddimage func toData (options: NSDictionary, type: CFString) -> Data? { guard let cgImage = cgImage else { return nil } return autoreleasepool { () -> Data? in let data = NSMutableData() guard let imageDestination = CGImageDestinationCreateWithData(data as CFMutableData, type, 1, nil) else { return nil } CGImageDestinationAddImage(imageDestination, cgImage, options) CGImageDestinationFinalize(imageDestination) return data as Data } } // https://developer.apple.com/documentation/mobilecoreservices/uttype/uti_image_content_types enum ImageType { case image // abstract image data case jpeg // JPEG image case jpeg2000 // JPEG-2000 image case tiff // TIFF image case pict // Quickdraw PICT format case gif // GIF image case png // PNG image case quickTimeImage // QuickTime image format (OSType 'qtif') case appleICNS // Apple icon data case bmp // Windows bitmap case ico // Windows icon data case rawImage // base type for raw image data (.raw) case scalableVectorGraphics // SVG image case livePhoto // Live Photo var value: CFString { switch self { case .image: return kUTTypeImage case .jpeg: return kUTTypeJPEG case .jpeg2000: return kUTTypeJPEG2000 case .tiff: return kUTTypeTIFF case .pict: return kUTTypePICT case .gif: return kUTTypeGIF case .png: return kUTTypePNG case .quickTimeImage: return kUTTypeQuickTimeImage case .appleICNS: return kUTTypeAppleICNS case .bmp: return kUTTypeBMP case .ico: return kUTTypeICO case .rawImage: return kUTTypeRawImage case .scalableVectorGraphics: return kUTTypeScalableVectorGraphics case .livePhoto: return kUTTypeLivePhoto } } } }
Using Solution 2.2
let compressionQuality: CGFloat = 0.4 guard let data = image.toJpegData(compressionQuality: compressionQuality) else { return } printSize(of: data) let options: NSDictionary = [ kCGImagePropertyHasAlpha: true, kCGImageDestinationLossyCompressionQuality: compressionQuality ] guard let data2 = image.toData(options: options, type: .png) else { return } printSize(of: data2)
Problems
Presenting an image will take up a lot of processor and memory resources. So, in this case, it is better to follow a few rules:
- do not run jpegData (compressionQuality :) in the main queue
- run only one jpegData (compressionQuality :) at a time
Wrong:
for i in 0...50 { DispatchQueue.global(qos: .utility).async { let quality = 0.02 * CGFloat(i) //let data = image.toJpegData(compressionQuality: quality) let data = image.jpegData(compressionQuality: quality) let size = CGFloat(data!.count)/1000.0/1024.0 print("\(i), quality: \(quality), \(size.rounded()) mb") } }
On right:
let serialQueue = DispatchQueue(label: "queue", qos: .utility, attributes: [], autoreleaseFrequency: .workItem, target: nil) for i in 0...50 { serialQueue.async { let quality = 0.02 * CGFloat(i) //let data = image.toJpegData(compressionQuality: quality) let data = image.jpegData(compressionQuality: quality) let size = CGFloat(data!.count)/1000.0/1024.0 print("\(i), quality: \(quality), \(size.rounded()) mb") } }
links