Skip to content

Instantly share code, notes, and snippets.

@tomasbasham
Last active February 1, 2024 19:04
Show Gist options
  • Star 89 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save tomasbasham/10533743 to your computer and use it in GitHub Desktop.
Save tomasbasham/10533743 to your computer and use it in GitHub Desktop.
Scale a UIImage to any given rect keeping the aspect ratio
@implementation UIImage (scale)
/**
* Scales an image to fit within a bounds with a size governed by
* the passed size. Also keeps the aspect ratio.
*
* Switch MIN to MAX for aspect fill instead of fit.
*
* @param newSize the size of the bounds the image must fit within.
* @return a new scaled image.
*/
- (UIImage *)scaleImageToSize:(CGSize)newSize {
CGRect scaledImageRect = CGRectZero;
CGFloat aspectWidth = newSize.width / self.size.width;
CGFloat aspectHeight = newSize.height / self.size.height;
CGFloat aspectRatio = MIN ( aspectWidth, aspectHeight );
scaledImageRect.size.width = self.size.width * aspectRatio;
scaledImageRect.size.height = self.size.height * aspectRatio;
scaledImageRect.origin.x = (newSize.width - scaledImageRect.size.width) / 2.0f;
scaledImageRect.origin.y = (newSize.height - scaledImageRect.size.height) / 2.0f;
UIGraphicsBeginImageContextWithOptions( newSize, NO, 0 );
[self drawInRect:scaledImageRect];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
@end
@cyberespia
Copy link

cyberespia commented Mar 25, 2018

And if the image is in a URL type http://domain.com/myimage.jpg obtained with JSON, as I do to resize it to show it in a UIImage so that it keeps the ratio?

@lee-burlak
Copy link

lee-burlak commented Jun 28, 2018

Thank you for your work, guys!

@akshaynhegde, @Oyvindkg, the method returns a blurry version of the original image, it is scaled, but blurry. Any idea how to fix it?

screen shot 2018-06-28 at 22 11 41

@shinrikiken
Copy link

Swift 4

extension UIImage {
  func scale(with size: CGSize) -> UIImage? {
    var scaledImageRect = CGRect.zero
    
    let aspectWidth:CGFloat = size.width / self.size.width
    let aspectHeight:CGFloat = size.height / self.size.height
    let aspectRatio:CGFloat = min(aspectWidth, aspectHeight)
    
    scaledImageRect.size.width = self.size.width * aspectRatio
    scaledImageRect.size.height = self.size.height * aspectRatio
    scaledImageRect.origin.x = (size.width - scaledImageRect.size.width) / 2.0
    scaledImageRect.origin.y = (size.height - scaledImageRect.size.height) / 2.0
    
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    
    self.draw(in: scaledImageRect)
    
    let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    return scaledImage
  }
}

@hemangshah
Copy link

@shinrikiken - your code is not giving me the perfect image which should fits to the image size.

@hemangshah
Copy link

hemangshah commented Apr 15, 2019

@lee-ho, perhaps you can replace this line

UIGraphicsBeginImageContext(newSize)

with this

UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale)

and try?

@fenixkim
Copy link

Does anyone have the version of this to work in NSImage (for MacOS)?

@fenixkim
Copy link

Swift 5 (Mac Os)

This working for me but I'm sure if is the best approach

extension NSImage {
    
    /// Represents a scaling mode
    enum ScalingMode {
        case aspectFill
        case aspectFit
        
        /// Calculates the aspect ratio between two sizes
        ///
        /// - parameters:
        ///     - size:      the first size used to calculate the ratio
        ///     - otherSize: the second size used to calculate the ratio
        ///
        /// - return: the aspect ratio between the two sizes
        func aspectRatio(between size: CGSize, and otherSize: CGSize) -> CGFloat {
            let aspectWidth  = size.width/otherSize.width
            let aspectHeight = size.height/otherSize.height
            
            switch self {
            case .aspectFill:
                return max(aspectWidth, aspectHeight)
            case .aspectFit:
                return min(aspectWidth, aspectHeight)
            }
        }
    }
    
    /// Scales an image to fit within a bounds with a size governed by the passed size. Also keeps the aspect ratio.
    ///
    /// - parameter:
    ///     - newSize:     the size of the bounds the image must fit within.
    ///     - scalingMode: the desired scaling mode
    ///
    /// - returns: a new scaled image.
    func scaled(to newSize: CGSize, scalingMode: ScalingMode = .aspectFill) -> NSImage {
        
        let aspectRatio = scalingMode.aspectRatio(between: newSize, and: size)
        
        /* Build the rectangle representing the area to be drawn */
        var scaledImageRect = CGRect.zero
        
        scaledImageRect.size.width  = size.width * aspectRatio
        scaledImageRect.size.height = size.height * aspectRatio
        scaledImageRect.origin.x    = (newSize.width - size.width * aspectRatio) / 2.0
        scaledImageRect.origin.y    = (newSize.height - size.height * aspectRatio) / 2.0
        
        let scaledImage = NSImage(size: newSize)
        scaledImage.lockFocus()
        draw(in: scaledImageRect)
        scaledImage.unlockFocus()
        
        return scaledImage
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment