Resize UIImage by keeping Aspect ratio and width
Asked Answered
V

19

149

I seen in many posts for resizing the image by keeping aspect ratio. These functions uses the fixed points(Width and Height) for RECT while resizing. But in my project, I need to resize the view based on the Width alone, Height should be taken automatically based on the aspect ratio. anyone help me to achieve this.

Voleta answered 4/10, 2011 at 8:59 Comment(0)
H
236

The method of Srikar works very well, if you know both height and width of your new Size. If you for example know only the width you want to scale to and don't care about the height you first have to calculate the scale factor of the height.

+(UIImage*)imageWithImage: (UIImage*) sourceImage scaledToWidth: (float) i_width
{
    float oldWidth = sourceImage.size.width;
    float scaleFactor = i_width / oldWidth;

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = oldWidth * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}
Hilaire answered 22/11, 2011 at 8:54 Comment(4)
You don't really need the newWidth variable here though, it's already i_width.Eschalot
You need to divide by height as well and compare the scaling factors using whichever is closer to 0. the code above will only work with images that are wider than they are highEpisiotomy
I'd just need to divide by height if i wanted to know my height ratio also and fit by either scaling the width or the height. but since the TO asked explicitly for keeping ratio and width, and not width or height, my answer is fully correct.Hilaire
Great answer, thanks. One more thing: if you experience any loss of image quality using this method then use UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, newHeight), NO, 0); rather then UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));Attenborough
A
58

If you don't happen to know if the image will be portrait or landscape (e.g user takes pic with camera), I created another method that takes max width and height parameters.

Lets say you have a UIImage *myLargeImage which is a 4:3 ratio.

UIImage *myResizedImage = [ImageUtilities imageWithImage:myLargeImage 
                                        scaledToMaxWidth:1024 
                                               maxHeight:1024];

The resized UIImage will be 1024x768 if landscape; 768x1024 if portrait. This method will also generate higher res images for retina display.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]);
    } else {
        UIGraphicsBeginImageContext(size);
    }
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();

    return newImage;
}

+ (UIImage *)imageWithImage:(UIImage *)image scaledToMaxWidth:(CGFloat)width maxHeight:(CGFloat)height {
    CGFloat oldWidth = image.size.width;
    CGFloat oldHeight = image.size.height;

    CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    CGFloat newHeight = oldHeight * scaleFactor;
    CGFloat newWidth = oldWidth * scaleFactor;
    CGSize newSize = CGSizeMake(newWidth, newHeight);

    return [ImageUtilities imageWithImage:image scaledToSize:newSize];
}
Aquarium answered 5/3, 2012 at 22:48 Comment(4)
This is the best thing but it always return double of my defined sizeProg
If you only want downsizing, not upsizing, don't forget to start the imageWithImage:scaledToMaxWidth:maxHeight: method with: if (image.size.width < width && image.size.height < height) { return image; }Solatium
To fit within a max width and max height, you need the minimum ratio as the scale factor: min(ratioX, ratioY). Otherwise, if width is larger, it may not fit the max height.Throne
ImageUtilities where it is?Antechoir
W
52

Best answer Maverick 1st's correctly translated to Swift (working with latest swift 3):

func imageWithImage (sourceImage:UIImage, scaledToWidth: CGFloat) -> UIImage {
    let oldWidth = sourceImage.size.width
    let scaleFactor = scaledToWidth / oldWidth

    let newHeight = sourceImage.size.height * scaleFactor
    let newWidth = oldWidth * scaleFactor

    UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
    sourceImage.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}
Wretch answered 5/12, 2015 at 9:54 Comment(2)
Keep in mind that using UIGraphicsBeginImageContext renders images at a scale of 1.0. You might want to use UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) when dealing with UI elements. It takes devices screen scale into account.Staffan
@Staffan No, UIGraphicsBeginImageContextWithOptions is deprecated. You can find more information to the official linkWretch
C
41

thanks @Maverick1st the algorithm, I implemented it to Swift, in my case height is the input parameter

class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {

    let scale = newHeight / image.size.height
    let newWidth = image.size.width * scale
    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
    image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
Captor answered 9/4, 2015 at 9:31 Comment(0)
L
20

Swift 5 version of aspect-fit-to-height, based on answer by @János

Uses the modern UIGraphicsImageRenderer API, so a valid UIImage is guaranteed to return.

extension UIImage
{
    /// Given a required height, returns a (rasterised) copy
    /// of the image, aspect-fitted to that height.

    func aspectFittedToHeight(_ newHeight: CGFloat) -> UIImage
    {
        let scale = newHeight / self.size.height
        let newWidth = self.size.width * scale
        let newSize = CGSize(width: newWidth, height: newHeight)
        let renderer = UIGraphicsImageRenderer(size: newSize)

        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}

You can use this in conjunction with a (vector-based) PDF image asset, to preserve quality at any render size.

Leathern answered 21/5, 2019 at 4:18 Comment(0)
K
19

This method is a category on UIImage. Does scale to fit in few lines of code using AVFoundation. Don't forget to import #import <AVFoundation/AVFoundation.h>.

@implementation UIImage (Helper)

- (UIImage *)imageScaledToFitToSize:(CGSize)size
{
    CGRect scaledRect = AVMakeRectWithAspectRatioInsideRect(self.size, CGRectMake(0, 0, size.width, size.height));
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [self drawInRect:scaledRect];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

@end
Kew answered 28/5, 2015 at 9:9 Comment(1)
Found this to give the best results for me with smallest memory footprint. +1Steinman
P
7

The simplest way is to set the frame of your UIImageView and set the contentMode to one of the resizing options.

The code goes like this - This can be used as an utility method -

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
    UIGraphicsBeginImageContext(newSize);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}
Pizarro answered 21/11, 2011 at 18:31 Comment(1)
Keep in mind the OP did not mention a UIImageView. Instead, he presumably wanted to aspect-fit a [copy of a] UIImage directly, which is a very handy thing to have when you're doing CoreGraphics work.Leathern
M
5

Just import AVFoundation and use AVMakeRectWithAspectRatioInsideRect(CGRectCurrentSize, CGRectMake(0, 0, YOUR_WIDTH, CGFLOAT_MAX)

Muscolo answered 30/4, 2014 at 12:0 Comment(0)
P
5

Well, we have few good answers here for resizing image, but I just modify as per my need. hope this help someone like me.

My Requirement was

  1. If image width is more than 1920, resize it with 1920 width and maintaining height with original aspect ratio.
  2. If image height is more than 1080, resize it with 1080 height and maintaining width with original aspect ratio.

 if (originalImage.size.width > 1920)
 {
        CGSize newSize;
        newSize.width = 1920;
        newSize.height = (1920 * originalImage.size.height) / originalImage.size.width;
        originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];        
 }

 if (originalImage.size.height > 1080)
 {
            CGSize newSize;
            newSize.width = (1080 * originalImage.size.width) / originalImage.size.height;
            newSize.height = 1080;
            originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];

 }

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

Thanks to @Srikar Appal , for resizing I have used his method.

You may like to check this as well for resize calculation.

Protocol answered 10/8, 2015 at 8:8 Comment(0)
L
4

Programmatically:

imageView.contentMode = UIViewContentModeScaleAspectFit;

Storyboard:

enter image description here

Letaletch answered 11/3, 2017 at 0:5 Comment(2)
it works, BUT reducing memory footprints, you can make app work faster, for example loading a lot of thumbnails.Picket
That's a UIImageView, not a UIImage. Sometimes, you need to do drawing without a UIImageView.Leathern
H
3

If somebody needs this solution in Swift 5:

private func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {
    
    let scale = newHeight / image.size.height
    let newWidth = image.size.width * scale
    UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
    image.draw(in:CGRect(x:0, y:0, width:newWidth, height:newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
Hild answered 3/8, 2020 at 11:27 Comment(0)
D
2

To improve on Ryan's answer:

   + (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
CGFloat oldWidth = image.size.width;
        CGFloat oldHeight = image.size.height;

//You may need to take some retina adjustments into consideration here
        CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

return [UIImage imageWithCGImage:image.CGImage scale:scaleFactor orientation:UIImageOrientationUp];
    }
Draughtboard answered 16/7, 2014 at 17:25 Comment(0)
O
2

Ryan's Solution @Ryan in swift code

use:

 func imageWithSize(image: UIImage,size: CGSize)->UIImage{
    if UIScreen.mainScreen().respondsToSelector("scale"){
        UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.mainScreen().scale);
    }
    else
    {
         UIGraphicsBeginImageContext(size);
    }

    image.drawInRect(CGRectMake(0, 0, size.width, size.height));
    var newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

//Summon this function VVV
func resizeImageWithAspect(image: UIImage,scaledToMaxWidth width:CGFloat,maxHeight height :CGFloat)->UIImage
{
    let oldWidth = image.size.width;
    let oldHeight = image.size.height;

    let scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    let newHeight = oldHeight * scaleFactor;
    let newWidth = oldWidth * scaleFactor;
    let newSize = CGSizeMake(newWidth, newHeight);

    return imageWithSize(image, size: newSize);
}
Outrush answered 9/7, 2015 at 14:27 Comment(0)
I
2
extension UIImage {

    /// Returns a image that fills in newSize
    func resizedImage(newSize: CGSize) -> UIImage? {
        guard size != newSize else { return self }
        
        let hasAlpha = false
        let scale: CGFloat = 0.0
        UIGraphicsBeginImageContextWithOptions(newSize, !hasAlpha, scale)
        
        draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
    
    /// Returns a resized image that fits in rectSize, keeping it's aspect ratio
    /// Note that the new image size is not rectSize, but within it.
    func resizedImageWithinRect(rectSize: CGSize) -> UIImage? {
        let widthFactor = size.width / rectSize.width
        let heightFactor = size.height / rectSize.height
        
        var resizeFactor = widthFactor
        if size.height > size.width {
            resizeFactor = heightFactor
        }
        
        let newSize = CGSize(width: size.width / resizeFactor, height: size.height / resizeFactor)
        let resized = resizedImage(newSize: newSize)
        
        return resized
    }
}

When scale is set to 0.0, the scale factor of the main screen is used, which for Retina displays is 2.0 or higher (3.0 on the iPhone 6 Plus).

Interlocutress answered 22/11, 2018 at 20:3 Comment(0)
S
1

In Swift 3 there are some changes. Here is an extension to UIImage:

public extension UIImage {
    public func resize(height: CGFloat) -> UIImage? {
        let scale = height / self.size.height
        let width = self.size.width * scale
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        self.draw(in: CGRect(x:0, y:0, width:width, height:height))
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }
}
Shopworn answered 1/9, 2017 at 8:43 Comment(0)
U
1

Calculates the best height of the image for available width.

import Foundation

public extension UIImage {
    public func height(forWidth width: CGFloat) -> CGFloat {
        let boundingRect = CGRect(
            x: 0,
            y: 0,
            width: width,
            height: CGFloat(MAXFLOAT)
        )
        let rect = AVMakeRect(
            aspectRatio: size,
            insideRect: boundingRect
        )
        return rect.size.height
    }
}
Ureter answered 10/4, 2018 at 13:19 Comment(1)
if anyone's is going for this answer "import AVFoundation" is gonna needed to make it work for Swift 5,Marillin
N
1

Zeeshan Tufail and Womble answers in Swift 5 with small improvements. Here we have extension with 2 functions to scale image to maxLength of any dimension and jpeg compression.

extension UIImage {
    func aspectFittedToMaxLengthData(maxLength: CGFloat, compressionQuality: CGFloat) -> Data {
        let scale = maxLength / max(self.size.height, self.size.width)
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: self.size, format: format)
        return renderer.jpegData(withCompressionQuality: compressionQuality) { context in
            self.draw(in: CGRect(origin: .zero, size: self.size))
        }
    }
    func aspectFittedToMaxLengthImage(maxLength: CGFloat, compressionQuality: CGFloat) -> UIImage? {
        let newImageData = aspectFittedToMaxLengthData(maxLength: maxLength, compressionQuality: compressionQuality)
        return UIImage(data: newImageData)
    }
}
Neves answered 12/12, 2019 at 7:58 Comment(0)
C
0

This one was perfect for me. Keeps aspect ratio and takes a maxLength. Width or Height will not be more than maxLength

-(UIImage*)imageWithImage: (UIImage*) sourceImage maxLength: (float) maxLength
{
    CGFloat scaleFactor = maxLength / MAX(sourceImage.size.width, sourceImage.size.height);

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = sourceImage.size.width * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
Crossstaff answered 30/12, 2017 at 4:11 Comment(0)
B
-1

I have solved it in a much simpler way

UIImage *placeholder = [UIImage imageNamed:@"OriginalImage.png"];
self.yourImageview.image = [UIImage imageWithCGImage:[placeholder CGImage] scale:(placeholder.scale * 1.5)
                                  orientation:(placeholder.imageOrientation)];

multiplier of the scale will define the scalling of the image,more the multiplier smaller the image. so You can check what fits your screen.

Or you can also get the multiplire by dividing imagewidth/screenwidth.

Brother answered 26/8, 2015 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.