簡體   English   中英

如何調整 UIImage 的大小以減小上傳圖片的大小

[英]How do I resize the UIImage to reduce upload image size

我一直在谷歌搜索,只遇到過降低高度/寬度或一些如何通過 CoreImage 編輯 UIImage 外觀的庫。 但我還沒有看到或找到一個庫,帖子解釋了如何減小圖像大小,所以當它上傳時,它不是完整的圖像大小。

到目前為止我有這個:

        if image != nil {
        //let data = NSData(data: UIImagePNGRepresentation(image))
        let data = UIImagePNGRepresentation(image)
        body.appendString("--\(boundary)\r\n")
        body.appendString("Content-Disposition: form-data; name=\"image\"; filename=\"randomName\"\r\n")
        body.appendString("Content-Type: image/png\r\n\r\n")
        body.appendData(data)
        body.appendString("\r\n")
    }

它正在發送 12MB 的照片。 我怎樣才能將它減少到 1mb? 謝謝!

Xcode 9 • Swift 4 或更高版本

編輯/更新:對於 iOS10+ 我們可以使用UIGraphicsImageRenderer 對於較舊的 Swift 語法,請檢查編輯歷史記錄。

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 resized(toWidth width: CGFloat, isOpaque: Bool = true) -> UIImage? {
        let canvas = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
        let format = imageRendererFormat
        format.opaque = isOpaque
        return UIGraphicsImageRenderer(size: canvas, format: format).image {
            _ in draw(in: CGRect(origin: .zero, size: canvas))
        }
    }
}

用法:

let image = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!

let thumb1 = image.resized(withPercentage: 0.1)
let thumb2 = image.resized(toWidth: 72.0)

這是我調整圖像大小所遵循的方式。

 -(UIImage *)resizeImage:(UIImage *)image
{
   float actualHeight = image.size.height;
   float actualWidth = image.size.width;
   float maxHeight = 300.0;
   float maxWidth = 400.0;
   float imgRatio = actualWidth/actualHeight;
   float maxRatio = maxWidth/maxHeight;
   float compressionQuality = 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;
    }
   }

   CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
   UIGraphicsBeginImageContext(rect.size);
   [image drawInRect:rect];
   UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
   NSData *imageData = UIImageJPEGRepresentation(img, compressionQuality);
   UIGraphicsEndImageContext();

   return [UIImage imageWithData:imageData];

}

使用這種方法,我的 6.5 MB 圖像減少到 104 KB。

斯威夫特 4 代碼:

func resize(_ image: UIImage) -> UIImage {
    var actualHeight = Float(image.size.height)
    var actualWidth = 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 = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
    UIGraphicsBeginImageContext(rect.size)
    image.draw(in: rect)
    let img = UIGraphicsGetImageFromCurrentImageContext()
    let imageData = img?.jpegData(compressionQuality: CGFloat(compressionQuality)) 
    UIGraphicsEndImageContext()
    return UIImage(data: imageData!) ?? UIImage()
}

如果有人正在尋找使用Swift 3 和 4 將圖像大小調整小於 1MB 的情況

只需復制並粘貼此擴展名:

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 = UIImagePNGRepresentation(self) else { return nil }

    var resizingImage = self
    var imageSizeKB = Double(imageData.count) / 1000.0 // ! Or devide for 1024 if you need KB but not kB

    while imageSizeKB > 1000 { // ! Or use 1024 if you need KB but not kB
        guard let resizedImage = resizingImage.resized(withPercentage: 0.9),
            let imageData = UIImagePNGRepresentation(resizedImage)
            else { return nil }

        resizingImage = resizedImage
        imageSizeKB = Double(imageData.count) / 1000.0 // ! Or devide for 1024 if you need KB but not kB
    }

    return resizingImage
}
}

使用:

let resizedImage = originalImage.resizedTo1MB()

編輯:請注意它會阻塞 UI ,因此如果您認為它適合您的情況,請移至后台線程

斯威夫特 5.4 和 Xcode 12.5

我對這里的解決方案不滿意,它根據給定的 KB 大小生成圖像,因為它們中的大多數使用.jpegData(compressionQuality: x) 此方法不適用於大圖像,因為即使將壓縮質量設置為 0.0,大圖像仍將保持較大,例如,在將compressionQuality 設置為 0.0 時,新 iPhone 的縱向模式生成的 10 MB 仍然會高於 1 MB。

因此,我在這里使用了一些答案並重寫了一個 Helper Struct,它可以在背景隊列中轉換圖像:

import UIKit

struct ImageCompressor {
    static func compress(image: UIImage, maxByte: Int,
                         completion: @escaping (UIImage?) -> ()) {
        DispatchQueue.global(qos: .userInitiated).async {
            guard let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else {
                return completion(nil)
            }
        
            var iterationImage: UIImage? = image
            var iterationImageSize = currentImageSize
            var iterationCompression: CGFloat = 1.0
        
            while iterationImageSize > maxByte && iterationCompression > 0.01 {
                let percantageDecrease = getPercantageToDecreaseTo(forDataCount: iterationImageSize)
            
                let canvasSize = CGSize(width: image.size.width * iterationCompression,
                                        height: image.size.height * iterationCompression)
                UIGraphicsBeginImageContextWithOptions(canvasSize, false, image.scale)
                defer { UIGraphicsEndImageContext() }
                image.draw(in: CGRect(origin: .zero, size: canvasSize))
                iterationImage = UIGraphicsGetImageFromCurrentImageContext()
            
                guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
                    return completion(nil)
                }
                iterationImageSize = newImageSize
                iterationCompression -= percantageDecrease
            }
            completion(iterationImage)
        }
    }

    private static func getPercantageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
        switch dataCount {
        case 0..<3000000: return 0.05
        case 3000000..<10000000: return 0.1
        default: return 0.2
        }
    }
}

將圖像壓縮到最大 1 MB:

        ImageCompressor.compress(image: image, maxByte: 1000000) { image in
            guard let compressedImage = image else { return }
            // Use compressedImage
        }
    }

與 Leo Answer 相同,但對 SWIFT 2.0 進行了少量編輯

 extension UIImage {
    func resizeWithPercentage(percentage: CGFloat) -> UIImage? {
        let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: size.width * percentage, height: size.height * percentage)))
        imageView.contentMode = .ScaleAspectFit
        imageView.image = self
        UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        imageView.layer.renderInContext(context)
        guard let result = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()
        return result
    }

    func resizeWithWidth(width: CGFloat) -> UIImage? {
        let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))))
        imageView.contentMode = .ScaleAspectFit
        imageView.image = self
        UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        imageView.layer.renderInContext(context)
        guard let result = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()
        return result
    }
}

這是 user4261201 的答案,但很快,我目前正在使用:

func compressImage (_ image: UIImage) -> UIImage {

    let actualHeight:CGFloat = image.size.height
    let actualWidth:CGFloat = image.size.width
    let imgRatio:CGFloat = actualWidth/actualHeight
    let maxWidth:CGFloat = 1024.0
    let resizedHeight:CGFloat = maxWidth/imgRatio
    let compressionQuality:CGFloat = 0.5

    let rect:CGRect = CGRect(x: 0, y: 0, width: maxWidth, height: resizedHeight)
    UIGraphicsBeginImageContext(rect.size)
    image.draw(in: rect)
    let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    let imageData:Data = UIImageJPEGRepresentation(img, compressionQuality)!
    UIGraphicsEndImageContext()

    return UIImage(data: imageData)!

}

斯威夫特4.2

  let imagedata = yourImage.jpegData(compressionQuality: 0.1)!

我認為這里問題的核心是如何在上傳到服務器之前可靠地將 UIImage 的數據縮小到一定大小,而不僅僅是縮小 UIImage 本身。

使用func jpegData(compressionQuality: CGFloat) -> Data? 如果您不需要壓縮到特定大小,則效果很好。 但是,在某些情況下,我發現能夠壓縮到某個指定的文件大小以下很有用。 在這種情況下, jpegData是不可靠的,並且以這種方式對圖像進行迭代壓縮會導致文件大小趨於平穩(並且可能非常昂貴)。 相反,我更喜歡像Leo 的回答那樣減小 UIImage 本身的大小,然后轉換為 jpegData 並反復檢查減小的大小是否低於我選擇的值(在我設置的邊距內)。 我根據當前文件大小與所需文件大小的比率調整壓縮步長乘數,以加速最昂貴的第一次迭代(因為此時文件大小最大)。

斯威夫特 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 {
        guard kb > 10 else { return Data() } // Prevents user from compressing below a limit (10kb in this case).
        let bytes = kb * 1024
        var compression: CGFloat = 1.0
        let step: CGFloat = 0.05
        var holderImage = self
        var complete = false
        while(!complete) {
            guard let data = holderImage.jpegData(compressionQuality: 1.0) else { break }
            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: 1000)

如果您以NSData格式上傳圖像,請使用以下命令:

NSData *imageData = UIImageJPEGRepresentation(yourImage, floatValue);

yourImage是你的UIImage floatvalue是壓縮值(0.0 到 1.0)

以上是將圖像轉換為JPEG

對於PNG使用: UIImagePNGRepresentation

注意:上面的代碼是在 Objective-C 中的。 請檢查如何在 Swift 中定義 NSData。

根據東芳的回答。 調整到特定的文件大小。 像 0.7 MB 一樣,您可以使用此代碼。

extension UIImage {

func resize(withPercentage percentage: CGFloat) -> UIImage? {
    var newRect = CGRect(origin: .zero, size: CGSize(width: size.width*percentage, height: size.height*percentage))
    UIGraphicsBeginImageContextWithOptions(newRect.size, true, 1)
    self.draw(in: newRect)
    defer {UIGraphicsEndImageContext()}
    return UIGraphicsGetImageFromCurrentImageContext()
}

func resizeTo(MB: Double) -> UIImage? {
    guard let fileSize = self.pngData()?.count else {return nil}
    let fileSizeInMB = CGFloat(fileSize)/(1024.0*1024.0)//form bytes to MB
    let percentage = 1/fileSizeInMB
    return resize(withPercentage: percentage)
}
}

使用它你可以控制你想要的大小:

func jpegImage(image: UIImage, maxSize: Int, minSize: Int, times: Int) -> Data? {
    var maxQuality: CGFloat = 1.0
    var minQuality: CGFloat = 0.0
    var bestData: Data?
    for _ in 1...times {
        let thisQuality = (maxQuality + minQuality) / 2
        guard let data = image.jpegData(compressionQuality: thisQuality) else { return nil }
        let thisSize = data.count
        if thisSize > maxSize {
            maxQuality = thisQuality
        } else {
            minQuality = thisQuality
            bestData = data
            if thisSize > minSize {
                return bestData
            }
        }
    }
    return bestData
}

方法調用示例:

jpegImage(image: image, maxSize: 500000, minSize: 400000, times: 10)  

它將嘗試在 maxSize 和 minSize 的最大和最小大小之間獲取文件,但只嘗試多次。 如果在這段時間內失敗,它將返回 nil。

我認為 swift 本身提供的最簡單的方法是將圖像壓縮成壓縮數據,下面是 swift 4.2 中的代碼

let imageData = yourImageTobeCompressed.jpegData(compressionQuality: 0.5)

你可以發送這個 imageData 上傳到服務器。

這就是我在 swift 3 中為調整 UIImage 大小所做的。 它將圖像大小減少到小於 100kb。 它按比例工作!

extension UIImage {
    class func scaleImageWithDivisor(img: UIImage, divisor: CGFloat) -> UIImage {
        let size = CGSize(width: img.size.width/divisor, height: img.size.height/divisor)
        UIGraphicsBeginImageContext(size)
        img.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return scaledImage!
    }
}

用法:

let scaledImage = UIImage.scaleImageWithDivisor(img: capturedImage!, divisor: 3)

在 Objective-C 中相同:

界面 :

@interface UIImage (Resize)

- (UIImage *)resizedWithPercentage:(CGFloat)percentage;
- (UIImage *)resizeTo:(CGFloat)weight isPng:(BOOL)isPng jpegCompressionQuality:(CGFloat)compressionQuality;

@end

執行 :

#import "UIImage+Resize.h"

@implementation UIImage (Resize)

- (UIImage *)resizedWithPercentage:(CGFloat)percentage {
    CGSize canvasSize = CGSizeMake(self.size.width * percentage, self.size.height * percentage);
    UIGraphicsBeginImageContextWithOptions(canvasSize, false, self.scale);
    [self drawInRect:CGRectMake(0, 0, canvasSize.width, canvasSize.height)];
    UIImage *sizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return sizedImage;
}

- (UIImage *)resizeTo:(CGFloat)weight isPng:(BOOL)isPng jpegCompressionQuality:(CGFloat)compressionQuality {
    NSData *imageData = isPng ? UIImagePNGRepresentation(self) : UIImageJPEGRepresentation(self, compressionQuality);
    if (imageData && [imageData length] > 0) {
        UIImage *resizingImage = self;
        double imageSizeKB = [imageData length] / weight;

        while (imageSizeKB > weight) {
            UIImage *resizedImage = [resizingImage resizedWithPercentage:0.9];
            imageData = isPng ? UIImagePNGRepresentation(resizedImage) : UIImageJPEGRepresentation(resizedImage, compressionQuality);
            resizingImage = resizedImage;
            imageSizeKB = (double)(imageData.length / weight);
        }

        return resizingImage;
    }
    return nil;
}

用法 :

#import "UIImage+Resize.h"

UIImage *resizedImage = [self.picture resizeTo:2048 isPng:NO jpegCompressionQuality:1.0];

當我嘗試使用公認的答案來調整我的項目中使用的圖像大小時,它會非常像素化和模糊。 我最終用這段代碼調整圖像大小而不添加像素化或模糊:

func scale(withPercentage percentage: CGFloat)-> UIImage? {
        let cgSize = CGSize(width: size.width * percentage, height: size.height * percentage)

        let hasAlpha = true
        let scale: CGFloat = 0.0 // Use scale factor of main screen

        UIGraphicsBeginImageContextWithOptions(cgSize, !hasAlpha, scale)
        self.draw(in: CGRect(origin: CGPoint.zero, size: cgSize))

        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
        return scaledImage
    }

我在研究 Swift 中的圖像壓縮和導出時遇到了這個問題,並以此為起點更好地理解問題並推導出更好的技術。

UIGraphicsBeginImageContext()、UIGraphicsGetImageFromCurrentImageContext()、UIGraphicsEndImageContext() 過程是一種較舊的技術,已被 UIGraphicsImageRenderer 取代,如Iron_john_bonneyleo-dabus 所使用 他們的例子是作為 UIImage 的擴展編寫的,而我選擇編寫一個獨立的函數。 可以通過比較(查看 UIGraphicsImageRenderer 調用及其附近)來識別方法中所需的差異,並且可以輕松地將其移植回 UIImage 擴展。

我認為這里使用的壓縮算法有改進的潛力,所以我采取了一種方法,首先將圖像調整為具有給定的總像素數,然后通過調整 jpeg 壓縮來壓縮它以獲得指定的最終文件大小. 指定像素總數的目的是避免陷入圖像縱橫比問題。 雖然我沒有做過詳盡的調查,但我懷疑將圖像縮放到指定的總像素數會將最終的 jpeg 圖像文件大小置於一般范圍內,然后可以使用 jpeg 壓縮來確保文件大小限制以可接受的圖像質量實現,前提是初始像素數不太高。

使用 UIGraphicsImageRenderer 時,CGRect 在主機 Apple 設備上以邏輯像素指定,這與輸出 jpeg 中的實際像素不同。 查找設備像素比率以了解這一點。 為了獲得設備像素比,我嘗試從環境中提取它,但這些技術導致操場崩潰,所以我使用了一種效率較低的技術。

如果將此代碼粘貼到 Xcode playround 並在 Resources 文件夾中放置適當的 .jpg 文件,則輸出文件將放置在 Playground 輸出文件夾中(使用實時視圖中的快速查看來查找此位置)。

import UIKit

func compressUIImage(_ image: UIImage?, numPixels: Int, fileSizeLimitKB: Double, exportImage: Bool) -> Data {
    var returnData: Data
    if let origWidth = image?.size.width,
       let origHeight = image?.size.height {
        print("Original image size =", origWidth, "*", origHeight, "pixels")

        let imgMult = min(sqrt(CGFloat(numPixels)/(origWidth * origHeight)), 1) // This multiplier scales the image to have the desired number of pixels
        print("imageMultiplier =", imgMult)

        let cgRect = CGRect(origin: .zero, size: CGSize(width: origWidth * imgMult, height: origHeight * imgMult)) // This is in *logical* pixels
        let renderer = UIGraphicsImageRenderer(size: cgRect.size)
        
        let img = renderer.image { ctx in
            image?.draw(in: cgRect)
        }
        
        // Now get the device pixel ratio if needed...
        var img_scale: CGFloat = 1
        if exportImage {
            img_scale = img.scale
        }
        print("Image scaling factor =", img_scale)

        // ...and use to ensure *output* image has desired number of pixels
        let cgRect_scaled = CGRect(origin: .zero, size: CGSize(width: origWidth * imgMult/img_scale, height: origHeight * imgMult/img_scale)) // This is in *logical* pixels
        print("New image size (in logical pixels) =", cgRect_scaled.width, "*", cgRect_scaled.height, "pixels") // Due to device pixel ratios, can have fractional pixel dimensions

        let renderer_scaled = UIGraphicsImageRenderer(size: cgRect_scaled.size)

        let img_scaled = renderer_scaled.image { ctx in
            image?.draw(in: cgRect_scaled)
        }

        var compQual = CGFloat(1.0)

        returnData = img_scaled.jpegData(compressionQuality: 1.0)!
        var imageSizeKB = Double(returnData.count) / 1000.0
        
        print("compressionQuality =", compQual, "=> imageSizeKB =", imageSizeKB, "KB")
        while imageSizeKB > fileSizeLimitKB {
            compQual *= 0.9
            returnData = img_scaled.jpegData(compressionQuality: compQual)!
            imageSizeKB = Double(returnData.count) / 1000.0
            print("compressionQuality =", compQual, "=> imageSizeKB =", imageSizeKB, "KB")
        }
    } else {
        returnData = Data()
    }
    return returnData
}

let image_orig = UIImage(named: "input.jpg")

let image_comp_data = compressUIImage(image_orig, numPixels: Int(4e6), fileSizeLimitKB: 1300, exportImage: true)

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

let filename = getDocumentsDirectory().appendingPathComponent("output.jpg")

try? image_comp_data.write(to: filename)

來源包括Jordan MorganHacking with Swift

iOS 15+ Swift 5

這里的部分解決方案沒有回答這個問題,因為它們沒有生成文件大小較小的圖像來上傳到后端。 在不需要時不要將大圖像文件上傳到后端非常重要。 它將占用更多空間,存儲成本更高,下載時間更長,導致 UI 等待內容。

很多答案都在使用

UIGraphicsImageRenderer(size: canvas).image {
    _ in draw(in: CGRect(origin: .zero, size: canvas))
}

或更老

UIGraphicsGetImageFromCurrentImageContext()

這些解決方案的問題是它們生成較小的UIImage ,但不會更改底層CGImage因此當您嘗試使用.jpegData(compressionQuality:)將圖像作為數據發送時,您會注意到上傳UIImage但來自底層CGImage的數據未調整大小且具有大文件大小。

UIImage -> CGImage Proxyman POST 圖片

其他解決方案強制將jpedData壓縮到最小可用值,這會產生非常大的壓縮和質量損失。

要使用所有底層內容實際調整圖像大小並將其作為非常小的最佳質量 jpeg 發送,請使用方法preparingThumbnail(of:)並將.jpegData(compressionQuality:)設置為 8 或 9。

extension UIImage {
    func thumbnail(width: CGFloat) -> UIImage? {
        guard size.width > width else { return self }
        let imageSize = CGSize(
            width: width,
            height: CGFloat(ceil(width/size.width * size.height))
        )
        return preparingThumbnail(of: imageSize)
    }
}

這是文檔preparingThumbnail(of:)

如果有人需要,這里是從Ali Pacman 的回答修改而來的異步版本:

import UIKit

extension UIImage {
    func compress(to maxByte: Int) async -> UIImage? {
        let compressTask = Task(priority: .userInitiated) { () -> UIImage? in
            guard let currentImageSize = jpegData(compressionQuality: 1.0)?.count else {
                return nil
            }

            var iterationImage: UIImage? = self
            var iterationImageSize = currentImageSize
            var iterationCompression: CGFloat = 1.0
            
            while iterationImageSize > maxByte && iterationCompression > 0.01 {
                let percentageDecrease = getPercentageToDecreaseTo(forDataCount: iterationImageSize)
                let canvasSize = CGSize(width: size.width * iterationCompression, height: size.height * iterationCompression)
                
                UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
                defer { UIGraphicsEndImageContext() }
                draw(in: CGRect(origin: .zero, size: canvasSize))
                iterationImage = UIGraphicsGetImageFromCurrentImageContext()
                
                guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
                    return nil
                }
                iterationImageSize = newImageSize
                iterationCompression -= percentageDecrease
            }
            
            return iterationImage
        }
        return await compressTask.value
    }
    
    private func getPercentageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
        switch dataCount {
        case 0..<3000000: return 0.05
        case 3000000..<10000000: return 0.1
        default: return 0.2
        }
    }
}

Swift 5.5 使用 async/await 和 image.pngData() 來獲取圖像的正確數據表示:


import UIKit

public struct ImageCompressor {
    
    private static func getPercentageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
           switch dataCount {
           case 0..<3000000: return 0.05
           case 3000000..<10000000: return 0.1
           default: return 0.2
           }
       }
    static public func compressAsync(image: UIImage, maxByte: Int) async -> UIImage? {
        guard let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else { return nil }
        var iterationImage: UIImage? = image
        var iterationImageSize = currentImageSize
        var iterationCompression: CGFloat = 1.0
        
        while iterationImageSize > maxByte && iterationCompression > 0.01 {
            let percentageDecrease = getPercentageToDecreaseTo(forDataCount: iterationImageSize)
            
            let canvasSize = CGSize(width: image.size.width * iterationCompression,
                                    height: image.size.height * iterationCompression)
            /*
            UIGraphicsBeginImageContextWithOptions(canvasSize, false, image.scale)
            defer { UIGraphicsEndImageContext() }
            image.draw(in: CGRect(origin: .zero, size: canvasSize))
            iterationImage = UIGraphicsGetImageFromCurrentImageContext()
            */
            iterationImage = await image.byPreparingThumbnail(ofSize: canvasSize)
            guard let newImageSize = iterationImage?.pngData()?.count else {
                return nil
            }
            iterationImageSize = newImageSize
            iterationCompression -= percentageDecrease
        }
        
        
        
        return iterationImage
    }
    
}

extension UIImage {
func resized(toValue value: CGFloat) -> UIImage {
    if size.width > size.height {
        return self.resize(toWidth: value)!
    } else {
        return self.resize(toHeight: value)!
    }
}

使用 .resizeToMaximumBytes 調整 UIImage 的大小

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM