How to easily resize/optimize an image size with iOS?

后端 未结 18 1224
温柔的废话
温柔的废话 2020-11-22 11:05

My application is downloading a set of image files from the network, and saving them to the local iPhone disk. Some of those images are pretty big in size (widths larger tha

相关标签:
18条回答
  • 2020-11-22 11:26

    Adding to the slew of answers here, but I have gone for a solution which resizes by file size, rather than dimensions.

    This will both reduce the dimensions and quality of the image until it reaches your given size.

    func compressTo(toSizeInMB size: Double) -> UIImage? {
        let bytes = size * 1024 * 1024
        let sizeInBytes = Int(bytes)
        var needCompress:Bool = true
        var imgData:Data?
        var compressingValue:CGFloat = 1.0
    
        while (needCompress) {
    
            if let resizedImage = scaleImage(byMultiplicationFactorOf: compressingValue), let data: Data = UIImageJPEGRepresentation(resizedImage, compressingValue) {
    
                if data.count < sizeInBytes || compressingValue < 0.1 {
                    needCompress = false
                    imgData = data
                } else {
                    compressingValue -= 0.1
                }
            }
        }
    
        if let data = imgData {
            print("Finished with compression value of: \(compressingValue)")
            return UIImage(data: data)
        }
        return nil
    }
    
    private func scaleImage(byMultiplicationFactorOf factor: CGFloat) -> UIImage? {
        let size = CGSize(width: self.size.width*factor, height: self.size.height*factor)
        UIGraphicsBeginImageContext(size)
        draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        if let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
            UIGraphicsEndImageContext()
            return newImage;
        }
        return nil
    }
    

    Credit for scaling by size answer

    0 讨论(0)
  • 2020-11-22 11:28

    I ended up using Brads technique to create a scaleToFitWidth method in UIImage+Extensions if that's useful to anyone...

    -(UIImage *)scaleToFitWidth:(CGFloat)width
    {
        CGFloat ratio = width / self.size.width;
        CGFloat height = self.size.height * ratio;
    
        NSLog(@"W:%f H:%f",width,height);
    
        UIGraphicsBeginImageContext(CGSizeMake(width, height));
        [self drawInRect:CGRectMake(0.0f,0.0f,width,height)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        return newImage;
    }
    

    then wherever you like

    #import "UIImage+Extensions.h"

    UIImage *newImage = [image scaleToFitWidth:100.0f];

    Also worth noting you could move this further down into a UIView+Extensions class if you want to render images from a UIView

    0 讨论(0)
  • 2020-11-22 11:30

    A problem that might occur on retina displays is that the scale of the image is set by ImageCapture or so. The resize functions above will not change that. In these cases the resize will work not properly.

    In the code below, the scale is set to 1 (not scaled) and the returned image has the size that you would expect. This is done in the UIGraphicsBeginImageContextWithOptions call.

    -(UIImage *)resizeImage :(UIImage *)theImage :(CGSize)theNewSize {
        UIGraphicsBeginImageContextWithOptions(theNewSize, NO, 1.0);
        [theImage drawInRect:CGRectMake(0, 0, theNewSize.width, theNewSize.height)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
    
    0 讨论(0)
  • 2020-11-22 11:32

    The above methods work well for small images, but when you try to resize a very large image, you will quickly run out of memory and crash the app. A much better way is to use CGImageSourceCreateThumbnailAtIndexto resize the image without completely decoding it first.

    If you have the path to the image you want to resize, you can use this:

    - (void)resizeImageAtPath:(NSString *)imagePath {
        // Create the image source (from path)
        CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);
    
        // To create image source from UIImage, use this
        // NSData* pngData =  UIImagePNGRepresentation(image);
        // CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);
    
        // Create thumbnail options
        CFDictionaryRef options = (__bridge CFDictionaryRef) @{
                (id) kCGImageSourceCreateThumbnailWithTransform : @YES,
                (id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                (id) kCGImageSourceThumbnailMaxPixelSize : @(640)
        };
        // Generate the thumbnail
        CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options); 
        CFRelease(src);
        // Write the thumbnail at path
        CGImageWriteToFile(thumbnail, imagePath);
    }
    

    More details here.

    0 讨论(0)
  • 2020-11-22 11:33

    A couple of suggestions are provided as answers to this question. I had suggested the technique described in this post, with the relevant code:

    + (UIImage*)imageWithImage:(UIImage*)image 
                   scaledToSize:(CGSize)newSize;
    {
       UIGraphicsBeginImageContext( newSize );
       [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
       UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
       UIGraphicsEndImageContext();
    
       return newImage;
    }
    

    As far as storage of the image, the fastest image format to use with the iPhone is PNG, because it has optimizations for that format. However, if you want to store these images as JPEGs, you can take your UIImage and do the following:

    NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);
    

    This creates an NSData instance containing the raw bytes for a JPEG image at a 60% quality setting. The contents of that NSData instance can then be written to disk or cached in memory.

    0 讨论(0)
  • 2020-11-22 11:34

    I developed an ultimate solution for image scaling in Swift.

    You can use it to resize image to fill, aspect fill or aspect fit specified size.

    You can align image to center or any of four edges and four corners.

    And also you can trim extra space which is added if aspect ratios of original image and target size are not equal.

    enum UIImageAlignment {
        case Center, Left, Top, Right, Bottom, TopLeft, BottomRight, BottomLeft, TopRight
    }
    
    enum UIImageScaleMode {
        case Fill,
        AspectFill,
        AspectFit(UIImageAlignment)
    }
    
    extension UIImage {
        func scaleImage(width width: CGFloat? = nil, height: CGFloat? = nil, scaleMode: UIImageScaleMode = .AspectFit(.Center), trim: Bool = false) -> UIImage {
            let preWidthScale = width.map { $0 / size.width }
            let preHeightScale = height.map { $0 / size.height }
            var widthScale = preWidthScale ?? preHeightScale ?? 1
            var heightScale = preHeightScale ?? widthScale
            switch scaleMode {
            case .AspectFit(_):
                let scale = min(widthScale, heightScale)
                widthScale = scale
                heightScale = scale
            case .AspectFill:
                let scale = max(widthScale, heightScale)
                widthScale = scale
                heightScale = scale
            default:
                break
            }
            let newWidth = size.width * widthScale
            let newHeight = size.height * heightScale
            let canvasWidth = trim ? newWidth : (width ?? newWidth)
            let canvasHeight = trim ? newHeight : (height ?? newHeight)
            UIGraphicsBeginImageContextWithOptions(CGSizeMake(canvasWidth, canvasHeight), false, 0)
    
            var originX: CGFloat = 0
            var originY: CGFloat = 0
            switch scaleMode {
            case .AspectFit(let alignment):
                switch alignment {
                case .Center:
                    originX = (canvasWidth - newWidth) / 2
                    originY = (canvasHeight - newHeight) / 2
                case .Top:
                    originX = (canvasWidth - newWidth) / 2
                case .Left:
                    originY = (canvasHeight - newHeight) / 2
                case .Bottom:
                    originX = (canvasWidth - newWidth) / 2
                    originY = canvasHeight - newHeight
                case .Right:
                    originX = canvasWidth - newWidth
                    originY = (canvasHeight - newHeight) / 2
                case .TopLeft:
                    break
                case .TopRight:
                    originX = canvasWidth - newWidth
                case .BottomLeft:
                    originY = canvasHeight - newHeight
                case .BottomRight:
                    originX = canvasWidth - newWidth
                    originY = canvasHeight - newHeight
                }
            default:
                break
            }
            self.drawInRect(CGRectMake(originX, originY, newWidth, newHeight))
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image
        }
    }
    

    There are examples of applying this solution below.

    Gray rectangle is target site image will be resized to. Blue circles in light blue rectangle is the image (I used circles because it's easy to see when it's scaled without preserving aspect). Light orange color marks areas that will be trimmed if you pass trim: true.

    Aspect fit before and after scaling:

    Another example of aspect fit:

    Aspect fit with top alignment:

    Aspect fill:

    Fill:

    I used upscaling in my examples because it's simpler to demonstrate but solution also works for downscaling as in question.

    For JPEG compression you should use this :

    let compressionQuality: CGFloat = 0.75 // adjust to change JPEG quality
    if let data = UIImageJPEGRepresentation(image, compressionQuality) {
      // ...
    }
    

    You can check out my gist with Xcode playground.

    0 讨论(0)
提交回复
热议问题