Я пытался загрузить файл изображения в Parse после того, как сфотографировал его прямо на телефон. Но это вызывает исключение:
Завершение работы приложения из-за неперехваченного исключения «NSInvalidArgumentException», причина: «PFFile не может быть больше 10485760 байт»
Вот мой код:
В контроллере первого представления:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "getImage")
{
var svc = segue.destinationViewController as! ClothesDetail
svc.imagePassed = imageView.image
}
}
В представлении контроллера, который загружает изображение:
let imageData = UIImagePNGRepresentation(imagePassed)
let imageFile = PFFile(name: "\(picName).png", data: imageData)
var userpic = PFObject(className:"UserPic")
userpic["picImage"] = imageFile`
Но мне все еще нужно загрузить это фото в Parse. Есть ли способ уменьшить размер или разрешение изображения?
ios
swift
parse-platform
uiimage
Elvislkm
источник
источник
Ответы:
Да, вы можете использовать
UIImageJPEGRepresentation
вместо,UIImagePNGRepresentation
чтобы уменьшить размер файла изображения. Вы можете просто создать расширение UIImage следующим образом:Xcode 8.2 • Swift 3.0.2
extension UIImage { enum JPEGQuality: CGFloat { case lowest = 0 case low = 0.25 case medium = 0.5 case high = 0.75 case highest = 1 } /// Returns the data for the specified image in JPEG format. /// If the image object’s underlying image data has been purged, calling this function forces that data to be reloaded into memory. /// - returns: A data object containing the JPEG data, or nil if there was a problem generating the data. This function may return nil if the image has no data or if the underlying CGImageRef contains data in an unsupported bitmap format. func jpeg(_ quality: JPEGQuality) -> Data? { return UIImageJPEGRepresentation(self, quality.rawValue) } }
изменить / обновить:
Xcode 10 Swift 4.2
extension UIImage { enum JPEGQuality: CGFloat { case lowest = 0 case low = 0.25 case medium = 0.5 case high = 0.75 case highest = 1 } /// Returns the data for the specified image in JPEG format. /// If the image object’s underlying image data has been purged, calling this function forces that data to be reloaded into memory. /// - returns: A data object containing the JPEG data, or nil if there was a problem generating the data. This function may return nil if the image has no data or if the underlying CGImageRef contains data in an unsupported bitmap format. func jpeg(_ jpegQuality: JPEGQuality) -> Data? { return jpegData(compressionQuality: jpegQuality.rawValue) } }
Применение:
if let imageData = image.jpeg(.lowest) { print(imageData.count) }
источник
UIImageJPEGRepresentation(yourImage, 1.0)
а не просто ввести.jp
и позволить xcode автозаполнить метод за вас. то же самое для перечисления сжатия.whatever
.Если вы хотите ограничить размер изображения некоторым конкретным значением, вы можете сделать следующее:
import UIKit extension UIImage { // MARK: - UIImage+Resize func compressTo(_ expectedSizeInMb:Int) -> UIImage? { let sizeInBytes = expectedSizeInMb * 1024 * 1024 var needCompress:Bool = true var imgData:Data? var compressingValue:CGFloat = 1.0 while (needCompress && compressingValue > 0.0) { if let data:Data = UIImageJPEGRepresentation(self, compressingValue) { if data.count < sizeInBytes { needCompress = false imgData = data } else { compressingValue -= 0.1 } } } if let data = imgData { if (data.count < sizeInBytes) { return UIImage(data: data) } } return nil } }
источник
if let data = bits.representation(using: .jpeg, properties: [.compressionFactor:compressingValue])
//image compression func resizeImage(image: UIImage) -> UIImage { var actualHeight: Float = Float(image.size.height) var actualWidth: Float = Float(image.size.width) let maxHeight: Float = 300.0 let maxWidth: Float = 400.0 var imgRatio: Float = actualWidth / actualHeight let maxRatio: Float = maxWidth / maxHeight let compressionQuality: Float = 0.5 //50 percent compression if actualHeight > maxHeight || actualWidth > maxWidth { if imgRatio < maxRatio { //adjust width according to maxHeight imgRatio = maxHeight / actualHeight actualWidth = imgRatio * actualWidth actualHeight = maxHeight } else if imgRatio > maxRatio { //adjust height according to maxWidth imgRatio = maxWidth / actualWidth actualHeight = imgRatio * actualHeight actualWidth = maxWidth } else { actualHeight = maxHeight actualWidth = maxWidth } } let rect = CGRectMake(0.0, 0.0, CGFloat(actualWidth), CGFloat(actualHeight)) UIGraphicsBeginImageContext(rect.size) image.drawInRect(rect) let img = UIGraphicsGetImageFromCurrentImageContext() let imageData = UIImageJPEGRepresentation(img!,CGFloat(compressionQuality)) UIGraphicsEndImageContext() return UIImage(data: imageData!)! }
источник
Jus Fixing для Xcode 7, протестирован 21.09.2015 и работает нормально:
Просто создайте расширение
UIImage
следующим образом:extension UIImage { var highestQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 1.0)! } var highQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.75)!} var mediumQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.5)! } var lowQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.25)!} var lowestQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 0.0)! } }
Тогда вы можете использовать это так:
let imageData = imagePassed.lowestQualityJPEGNSData
источник
Бинарный подход Swift 4 к сжатию изображения
Я считаю, что уже довольно поздно отвечать на этот вопрос, но вот мое решение вопроса, которое оптимизировано. Я использую двоичный поиск, чтобы найти оптимальное значение. Так, например, скажем, при обычном подходе вычитания для достижения 62% потребуется 38 попыток сжатия, подход * Двоичный поиск ** достигнет требуемого решения в max log (100) = около 7 попыток.
Тем не менее, я также хотел бы сообщить вам, что
UIImageJPEGRepresentation
функция не ведет себя линейно, особенно когда число приближается к 1. Вот снимок экрана, на котором мы видим, что изображение перестает сжиматься после того, как значение с плавающей запятой> 0,995. Поведение довольно непредсказуемо, поэтому лучше иметь дельта-буфер, который обрабатывал бы такие случаи.Вот код для этого
extension UIImage { func resizeToApprox(sizeInMB: Double, deltaInMB: Double = 0.2) -> Data { let allowedSizeInBytes = Int(sizeInMB * 1024 * 1024) let deltaInBytes = Int(deltaInMB * 1024 * 1024) let fullResImage = UIImageJPEGRepresentation(self, 1.0) if (fullResImage?.count)! < Int(deltaInBytes + allowedSizeInBytes) { return fullResImage! } var i = 0 var left:CGFloat = 0.0, right: CGFloat = 1.0 var mid = (left + right) / 2.0 var newResImage = UIImageJPEGRepresentation(self, mid) while (true) { i += 1 if (i > 13) { print("Compression ran too many times ") // ideally max should be 7 times as log(base 2) 100 = 6.6 break } print("mid = \(mid)") if ((newResImage?.count)! < (allowedSizeInBytes - deltaInBytes)) { left = mid } else if ((newResImage?.count)! > (allowedSizeInBytes + deltaInBytes)) { right = mid } else { print("loop ran \(i) times") return newResImage! } mid = (left + right) / 2.0 newResImage = UIImageJPEGRepresentation(self, mid) } return UIImageJPEGRepresentation(self, 0.5)! } }
источник
for i in 0...13
.Это очень просто с
UIImage
расширениемextension UIImage { func resizeByByte(maxByte: Int, completion: @escaping (Data) -> Void) { var compressQuality: CGFloat = 1 var imageData = Data() var imageByte = UIImageJPEGRepresentation(self, 1)?.count while imageByte! > maxByte { imageData = UIImageJPEGRepresentation(self, compressQuality)! imageByte = UIImageJPEGRepresentation(self, compressQuality)?.count compressQuality -= 0.1 } if maxByte > imageByte! { completion(imageData) } else { completion(UIImageJPEGRepresentation(self, 1)!) } }
использовать
// max 300kb image.resizeByByte(maxByte: 300000) { (resizedData) in print("image size: \(resizedData.count)") }
источник
Обновление Swift 4.2 . Я создал это расширение, чтобы уменьшить размер UIImage.
Здесь у вас есть два метода: первый берет процент, а второй уменьшает изображение до 1 МБ.
Конечно, вы можете изменить второй метод, чтобы он стал 1 КБ или любым другим размером.
import UIKit extension UIImage { func resized(withPercentage percentage: CGFloat) -> UIImage? { let canvasSize = CGSize(width: size.width * percentage, height: size.height * percentage) UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale) defer { UIGraphicsEndImageContext() } draw(in: CGRect(origin: .zero, size: canvasSize)) return UIGraphicsGetImageFromCurrentImageContext() } func resizedTo1MB() -> UIImage? { guard let imageData = self.pngData() else { return nil } let megaByte = 1000.0 var resizingImage = self var imageSizeKB = Double(imageData.count) / megaByte // ! Or devide for 1024 if you need KB but not kB while imageSizeKB > megaByte { // ! Or use 1024 if you need KB but not kB guard let resizedImage = resizingImage.resized(withPercentage: 0.5), let imageData = resizedImage.pngData() else { return nil } resizingImage = resizedImage imageSizeKB = Double(imageData.count) / megaByte // ! Or devide for 1024 if you need KB but not kB } return resizingImage } }
источник
В Swift 5 как ответ @Thiago Arreguy:
extension UIImage { var highestQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 1.0)! } var highQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.75)!} var mediumQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.5)! } var lowQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.25)!} var lowestQualityJPEGNSData: Data { return self.jpegData(compressionQuality: 0.0)! } }
А позвонить можно так:
let imageData = imagePassed.lowestQualityJPEGNSData
источник
В Swift
func ResizeImageFromOriginalSize(targetSize: CGSize) -> UIImage { var actualHeight: Float = Float(self.size.height) var actualWidth: Float = Float(self.size.width) let maxHeight: Float = Float(targetSize.height) let maxWidth: Float = Float(targetSize.width) var imgRatio: Float = actualWidth / actualHeight let maxRatio: Float = maxWidth / maxHeight var compressionQuality: Float = 0.5 //50 percent compression if actualHeight > maxHeight || actualWidth > maxWidth { if imgRatio < maxRatio { //adjust width according to maxHeight imgRatio = maxHeight / actualHeight actualWidth = imgRatio * actualWidth actualHeight = maxHeight } else if imgRatio > maxRatio { //adjust height according to maxWidth imgRatio = maxWidth / actualWidth actualHeight = imgRatio * actualHeight actualWidth = maxWidth } else { actualHeight = maxHeight actualWidth = maxWidth compressionQuality = 1.0 } } let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight)) UIGraphicsBeginImageContextWithOptions(rect.size, false, CGFloat(compressionQuality)) self.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! }
источник
Использование
func jpegData(compressionQuality: CGFloat) -> Data?
хорошо работает, если вам не нужно сжимать до определенного размера. Однако для некоторых изображений я считаю полезным иметь возможность сжимать файл ниже определенного размера. В этом случаеjpegData
это ненадежно, и итеративное сжатие изображения приводит к снижению размера файла (и может быть очень дорогостоящим). Вместо этого я предпочитаю уменьшить размер самого UIImage, затем преобразовать его в jpegData и проверить, не меньше ли уменьшенный размер выбранному мной значению (в пределах установленной мной погрешности). Я регулирую множитель шага сжатия на основе отношения текущего размера файла к желаемому, чтобы ускорить первые итерации, которые являются самыми дорогими.Swift 5
extension UIImage { func resized(withPercentage percentage: CGFloat, isOpaque: Bool = true) -> UIImage? { let canvas = CGSize(width: size.width * percentage, height: size.height * percentage) let format = imageRendererFormat format.opaque = isOpaque return UIGraphicsImageRenderer(size: canvas, format: format).image { _ in draw(in: CGRect(origin: .zero, size: canvas)) } } func compress(to kb: Int, allowedMargin: CGFloat = 0.2) -> Data { let bytes = kb * 1024 var compression: CGFloat = 1.0 let step: CGFloat = 0.05 var holderImage = self var complete = false while(!complete) { if let data = holderImage.jpegData(compressionQuality: 1.0) { let ratio = data.count / bytes if data.count < Int(CGFloat(bytes) * (1 + allowedMargin)) { complete = true return data } else { let multiplier:CGFloat = CGFloat((ratio / 5) + 1) compression -= (step * multiplier) } } guard let newImage = holderImage.resized(withPercentage: compression) else { break } holderImage = newImage } return Data() } }
И использование:
let data = image.compress(to: 300)
resized
Расширение UIImage происходит от: Как изменить размер UIImage, чтобы уменьшить размер загружаемого изображенияисточник
Swift 3
Ответ @ leo-dabus пересмотрен для быстрого 3
extension UIImage { var uncompressedPNGData: Data? { return UIImagePNGRepresentation(self) } var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0) } var highQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.75) } var mediumQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.5) } var lowQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.25) } var lowestQualityJPEGNSData:Data? { return UIImageJPEGRepresentation(self, 0.0) } }
источник
В Swift 4 я создал это расширение, которое получит ожидаемый размер при попытке его достичь.
extension UIImage { enum CompressImageErrors: Error { case invalidExSize case sizeImpossibleToReach } func compressImage(_ expectedSizeKb: Int, completion : (UIImage,CGFloat) -> Void ) throws { let minimalCompressRate :CGFloat = 0.4 // min compressRate to be checked later if expectedSizeKb == 0 { throw CompressImageErrors.invalidExSize // if the size is equal to zero throws } let expectedSizeBytes = expectedSizeKb * 1024 let imageToBeHandled: UIImage = self var actualHeight : CGFloat = self.size.height var actualWidth : CGFloat = self.size.width var maxHeight : CGFloat = 841 //A4 default size I'm thinking about a document var maxWidth : CGFloat = 594 var imgRatio : CGFloat = actualWidth/actualHeight let maxRatio : CGFloat = maxWidth/maxHeight var compressionQuality : CGFloat = 1 var imageData:Data = UIImageJPEGRepresentation(imageToBeHandled, compressionQuality)! while imageData.count > expectedSizeBytes { if (actualHeight > maxHeight || actualWidth > maxWidth){ if(imgRatio < maxRatio){ imgRatio = maxHeight / actualHeight; actualWidth = imgRatio * actualWidth; actualHeight = maxHeight; } else if(imgRatio > maxRatio){ imgRatio = maxWidth / actualWidth; actualHeight = imgRatio * actualHeight; actualWidth = maxWidth; } else{ actualHeight = maxHeight; actualWidth = maxWidth; compressionQuality = 1; } } let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight) UIGraphicsBeginImageContext(rect.size); imageToBeHandled.draw(in: rect) let img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); if let imgData = UIImageJPEGRepresentation(img!, compressionQuality) { if imgData.count > expectedSizeBytes { if compressionQuality > minimalCompressRate { compressionQuality -= 0.1 } else { maxHeight = maxHeight * 0.9 maxWidth = maxWidth * 0.9 } } imageData = imgData } } completion(UIImage(data: imageData)!, compressionQuality) } }
Использовать
do { try UiImageView.image?.compressImage(100, completion: { (image, compressRatio) in print(image.size) imageData = UIImageJPEGRepresentation(image, compressRatio) base64data = imageData?.base64EncodedString() }) } catch { print("Error") }
источник