The simplest way to resize an UIImage? - ios
In my iPhone app, I take a picture with the camera, then I want to resize it to 290*390 pixels. I was using this method to resize the image :
UIImage *newImage = [image _imageScaledToSize:CGSizeMake(290, 390)
interpolationQuality:1];
It works perfectly, but it's an undocumented function, so I can't use it anymore with iPhone OS4.
So... what is the simplest way to resize an UIImage ?
The simplest way is to set the frame of your UIImageView and set the contentMode to one of the resizing options.
Or you can use this utility method, if you actually need to resize an image:
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Example usage:
#import "MYUtil.h"
…
UIImage *myIcon = [MYUtil imageWithImage:myUIImageInstance scaledToSize:CGSizeMake(20, 20)];
Proper Swift 3.0 for iOS 10+ solution: Using ImageRenderer and closure syntax:
func imageWith(newSize: CGSize) -> UIImage {
let image = UIGraphicsImageRenderer(size: newSize).image { _ in
draw(in: CGRect(origin: .zero, size: newSize))
}
return image.withRenderingMode(renderingMode)
}
And here's the Objective-C version:
#implementation UIImage (ResizeCategory)
- (UIImage *)imageWithSize:(CGSize)newSize
{
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:newSize];
UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext*_Nonnull myContext) {
[self drawInRect:(CGRect) {.origin = CGPointZero, .size = newSize}];
}];
return [image imageWithRenderingMode:self.renderingMode];
}
#end
Here's a Swift version of Paul Lynch's answer
func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
And as an extension:
public extension UIImage {
func copy(newSize: CGSize, retina: Bool = true) -> UIImage? {
// In next line, pass 0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(
/* size: */ newSize,
/* opaque: */ false,
/* scale: */ retina ? 0 : 1
)
defer { UIGraphicsEndImageContext() }
self.draw(in: CGRect(origin: .zero, size: newSize))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
A more compact version for Swift 4 and iOS 10+:
extension UIImage {
func resized(to size: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { _ in
draw(in: CGRect(origin: .zero, size: size))
}
}
}
Usage:
let resizedImage = image.resized(to: CGSize(width: 50, height: 50))
Swift solution for Stretch Fill, Aspect Fill and Aspect Fit
extension UIImage {
enum ContentMode {
case contentFill
case contentAspectFill
case contentAspectFit
}
func resize(withSize size: CGSize, contentMode: ContentMode = .contentAspectFill) -> UIImage? {
let aspectWidth = size.width / self.size.width
let aspectHeight = size.height / self.size.height
switch contentMode {
case .contentFill:
return resize(withSize: size)
case .contentAspectFit:
let aspectRatio = min(aspectWidth, aspectHeight)
return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
case .contentAspectFill:
let aspectRatio = max(aspectWidth, aspectHeight)
return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
}
}
private func resize(withSize size: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
and to use you can do the following:
let image = UIImage(named: "image.png")!
let newImage = image.resize(withSize: CGSize(width: 200, height: 150), contentMode: .contentAspectFill)
Thanks to abdullahselek for his original solution.
Trevor Howard has some UIImage categories that handle resize quite nicely. If nothing else you can use the code as examples.
Note: As of iOS 5.1, this answer maybe invalid. See comment below.
I've also seen this done as well (which I use on UIButtons for Normal and Selected state since buttons don't resize to fit). Credit goes to whoever the original author was.
First make an empty .h and .m file called UIImageResizing.h and UIImageResizing.m
// Put this in UIImageResizing.h
#interface UIImage (Resize)
- (UIImage*)scaleToSize:(CGSize)size;
#end
// Put this in UIImageResizing.m
#implementation UIImage (Resize)
- (UIImage*)scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.width, size.height), self.CGImage);
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
#end
Include that .h file in whatever .m file you're going to use the function in and then call it like this:
UIImage* image = [UIImage imageNamed:#"largeImage.png"];
UIImage* smallImage = [image scaleToSize:CGSizeMake(100.0f,100.0f)];
This improvement to Paul's code will give you a sharp high res image on an iPhone with a retina display. Otherwise when scaling down it's blurry.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);
} else {
UIGraphicsBeginImageContext(newSize);
}
} else {
UIGraphicsBeginImageContext(newSize);
}
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Here is a simple way:
UIImage * image = [UIImage imageNamed:#"image"];
CGSize sacleSize = CGSizeMake(10, 10);
UIGraphicsBeginImageContextWithOptions(sacleSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, sacleSize.width, sacleSize.height)];
UIImage * resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
resizedImage is a new image.
Here's a modification of the category written by iWasRobbed above. It keeps the aspect ratio of the original image instead of distorting it.
- (UIImage*)scaleToSizeKeepAspect:(CGSize)size {
UIGraphicsBeginImageContext(size);
CGFloat ws = size.width/self.size.width;
CGFloat hs = size.height/self.size.height;
if (ws > hs) {
ws = hs/ws;
hs = 1.0;
} else {
hs = ws/hs;
ws = 1.0;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(size.width/2-(size.width*ws)/2,
size.height/2-(size.height*hs)/2, size.width*ws,
size.height*hs), self.CGImage);
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
Why so complicated? I think using system API can achieve the same result:
UIImage *largeImage;
CGFloat ratio = 0.4; // you want to get a new image that is 40% the size of large image.
UIImage *newImage = [UIImage imageWithCGImage:largeImage.CGImage
scale:1/ratio
orientation:largeImage.imageOrientation];
// notice the second argument, it is 1/ratio, not ratio.
The only gotcha is you should pass inverse of target ratio as the second argument, as according to the document the second parameter specifies the ratio of original image compared to the new scaled one.
For Swift 5:
extension UIImage {
func resized(to newSize: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(origin: .zero, size: newSize))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
If you just want an image smaller and don't care about exact size:
+ (UIImage *)imageWithImage:(UIImage *)image scaledToScale:(CGFloat)scale
{
UIGraphicsBeginImageContextWithOptions(self.size, YES, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Setting scale to 0.25f will give you a 816 by 612 image from a 8MP camera.
Here's a category UIImage+Scale for those who needs one.
This is an UIImage extension compatible with Swift 3 and Swift 4 which scales image to given size with an aspect ratio
extension UIImage {
func scaledImage(withSize size: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
return UIGraphicsGetImageFromCurrentImageContext()!
}
func scaleImageToFitSize(size: CGSize) -> UIImage {
let aspect = self.size.width / self.size.height
if size.width / aspect <= size.height {
return scaledImage(withSize: CGSize(width: size.width, height: size.width / aspect))
} else {
return scaledImage(withSize: CGSize(width: size.height * aspect, height: size.height))
}
}
}
Example usage
let image = UIImage(named: "apple")
let scaledImage = image.scaleImageToFitSize(size: CGSize(width: 45.0, height: 45.0))
When using iOS 15 or newer, you can use the new prepareThumbnail method of UIImage:
sourceImage.prepareThumbnail(of: thumbnailSize) { thumbnail in
// Do something with the resized image
DispatchQueue.main.async {
cell.imageView?.image = thumbnail
}
}
More info here:
https://developer.apple.com/documentation/uikit/uiimage/3750845-preparethumbnail
I found a category for UIImage in Apple's own examples which does the same trick. Here's the link: https://developer.apple.com/library/ios/samplecode/sc2273/Listings/AirDropSample_UIImage_Resize_m.html.
You'll just have to change the call:
UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);
in imageWithImage:scaledToSize:inRect: with:
UIGraphicsBeginImageContextWithOptions(newSize, NO, 2.0);
In order to consider the alpha channel in the image.
For my fellow Xamarians, here is a Xamarin.iOS C# version of #Paul Lynch answer.
private UIImage ResizeImage(UIImage image, CGSize newSize)
{
UIGraphics.BeginImageContextWithOptions(newSize, false, 0.0f);
image.Draw(new CGRect(0, 0, newSize.Width, newSize.Height));
UIImage newImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return newImage;
}
func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage
{
let scale = newWidth / image.size.width
let newHeight = image.size.height * scale
UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Effective approach without stretching image Swift 4
// Method to resize image
func resize(image: UIImage, toScaleSize:CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(toScaleSize, true, image.scale)
image.draw(in: CGRect(x: 0, y: 0, width: toScaleSize.width, height: toScaleSize.height))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage!
}
// Call method
let resizedImage = self.resize(image: UIImage(named: "YourImageName")!, toScaleSize: CGSize(width: 290, height: 390))
If you want to make a thumbnail of a UIImage (with proportional resizing or maybe some cropping involved), check out UIImage+Resize category that allows you to use concise, ImageMagick-like syntax:
UIImage* squareImage = [image resizedImageByMagick: #"320x320#"];
[cf Chris] To resize to a desired size:
UIImage *after = [UIImage imageWithCGImage:before.CGImage
scale:CGImageGetHeight(before.CGImage)/DESIREDHEIGHT
orientation:UIImageOrientationUp];
or, equivalently, substitute CGImageGetWidth(...)/DESIREDWIDTH
Rogerio Chaves answer as a swift extension
func scaledTo(size: CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
self.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
And also bonus
func scaledTo(height: CGFloat) -> UIImage{
let width = height*self.size.width/self.size.height
return scaledTo(size: CGSize(width: width, height: height))
}
Swift 3.0 with failsafe option (returns the original image in case of error):
func resize(image: UIImage, toSize size: CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(size,false,1.0)
image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
if let resizedImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return resizedImage
}
UIGraphicsEndImageContext()
return image
}
(Swift 4 compatible) iOS 10+ and iOS < 10 solution (using UIGraphicsImageRenderer if possible, UIGraphicsGetImageFromCurrentImageContext otherwise)
/// Resizes an image
///
/// - Parameter newSize: New size
/// - Returns: Resized image
func scaled(to newSize: CGSize) -> UIImage {
let rect = CGRect(origin: .zero, size: newSize)
if #available(iOS 10, *) {
let renderer = UIGraphicsImageRenderer(size: newSize)
return renderer.image { _ in
self.draw(in: rect)
}
} else {
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
#Paul Lynch's answer is great, but it would change the image ratio.
if you don`t want to change the image ratio, and still want the new image fit for new size, try this.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
// calculate a new size which ratio is same to original image
CGFloat ratioW = image.size.width / newSize.width;
CGFloat ratioH = image.size.height / newSize.height;
CGFloat ratio = image.size.width / image.size.height;
CGSize showSize = CGSizeZero;
if (ratioW > 1 && ratioH > 1) {
if (ratioW > ratioH) {
showSize.width = newSize.width;
showSize.height = showSize.width / ratio;
} else {
showSize.height = newSize.height;
showSize.width = showSize.height * ratio;
}
} else if (ratioW > 1) {
showSize.width = showSize.width;
showSize.height = showSize.width / ratio;
} else if (ratioH > 1) {
showSize.height = showSize.height;
showSize.width = showSize.height * ratio;
}
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(showSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, showSize.width, showSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;}
use this extension
extension UIImage {
public func resize(size:CGSize, completionHandler:(resizedImage:UIImage, data:NSData?)->()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
let newSize:CGSize = size
let rect = CGRectMake(0, 0, newSize.width, newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.drawInRect(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let imageData = UIImageJPEGRepresentation(newImage, 0.5)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(resizedImage: newImage, data:imageData)
})
})
}
}
Some time your image have scale large than 1 so that resize image will make an image unexpected. This is my solution for this case.
extension UIImage {
func resizeTo(newSize: CGSize) -> UIImage {
// Important thing here
let format = UIGraphicsImageRendererFormat()
format.scale = 1
let image = UIGraphicsImageRenderer(size: newSize, format: format).image { _ in
draw(in: CGRect(origin: .zero, size: newSize))
}
return image.withRenderingMode(renderingMode)
}
}
Swift 2.0 :
let image = UIImage(named: "imageName")
let newSize = CGSize(width: 10, height: 10)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image?.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let imageResized = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Here my somewhat-verbose Swift code
func scaleImage(image:UIImage, toSize:CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(toSize, false, 0.0);
let aspectRatioAwareSize = self.aspectRatioAwareSize(image.size, boxSize: toSize, useLetterBox: false)
let leftMargin = (toSize.width - aspectRatioAwareSize.width) * 0.5
let topMargin = (toSize.height - aspectRatioAwareSize.height) * 0.5
image.drawInRect(CGRectMake(leftMargin, topMargin, aspectRatioAwareSize.width , aspectRatioAwareSize.height))
let retVal = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return retVal
}
func aspectRatioAwareSize(imageSize: CGSize, boxSize: CGSize, useLetterBox: Bool) -> CGSize {
// aspect ratio aware size
// http://stackoverflow.com/a/6565988/8047
let imageWidth = imageSize.width
let imageHeight = imageSize.height
let containerWidth = boxSize.width
let containerHeight = boxSize.height
let imageAspectRatio = imageWidth/imageHeight
let containerAspectRatio = containerWidth/containerHeight
let retVal : CGSize
// use the else at your own risk: it seems to work, but I don't know
// the math
if (useLetterBox) {
retVal = containerAspectRatio > imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
} else {
retVal = containerAspectRatio < imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
}
return retVal
}
Swift 4 answer:
func scaleDown(image: UIImage, withSize: CGSize) -> UIImage {
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(withSize, false, scale)
image.draw(in: CGRect(x: 0, y: 0, width: withSize.width, height: withSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
Related
How do change the size of an image (not the size of the file, not the size of an image view) in iOS? [duplicate]
I am making an app for iOS, using Swift and Parse.com I am trying to let the user select a picture from an image picker and then resize the selected image to 200x200 pixels before uploading to my backend. Parse.com have a tutorial for an Instagram copy app called "AnyPic" which gives this code for resizing images, but it is in Objective-C.... // Resize the image to be square (what is shown in the preview) UIImage *resizedImage = [anImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:CGSizeMake(560.0f, 560.0f) interpolationQuality:kCGInterpolationHigh]; // Create a thumbnail and add a corner radius for use in table views UIImage *thumbnailImage = [anImage thumbnailImage:86.0f transparentBorder:0.0f cornerRadius:10.0f interpolationQuality:kCGInterpolationDefault]; How would I create a 200x200px version of the selected picture (to then upload) in Swift? And, what is the thumbnailImage function doing?
See my blog post, Resize image in swift and objective C, for further details. Image resize function in swift as below. func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage? { let size = image.size let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height // Figure out what our orientation is, and use that to form the rectangle var newSize: CGSize if(widthRatio > heightRatio) { newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) } else { newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) } // This is the rect that we've calculated out and this is what is actually used below let rect = CGRect(origin: .zero, size: newSize) // Actually do the resizing to the rect using the ImageContext stuff UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } Use the above function and resize image with 200*200 as below code self.resizeImage(UIImage(named: "yourImageName")!, targetSize: CGSizeMake(200.0, 200.0)) swift3 updated func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage { let size = image.size let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height // Figure out what our orientation is, and use that to form the rectangle var newSize: CGSize if(widthRatio > heightRatio) { newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) } else { newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) } // This is the rect that we've calculated out and this is what is actually used below let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) // Actually do the resizing to the rect using the ImageContext stuff UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! }
Details Xcode 10.2.1 (10E1001), Swift 5 Links https://gist.github.com/eugenebokhan/5e62a0155754ae6aa6c3c13cf1744930 Image Resizing Techniques Solution import UIKit import CoreGraphics import Accelerate extension UIImage { public enum ResizeFramework { case uikit, coreImage, coreGraphics, imageIO, accelerate } /// Resize image with ScaleAspectFit mode and given size. /// /// - Parameter dimension: width or length of the image output. /// - Parameter resizeFramework: Technique for image resizing: UIKit / CoreImage / CoreGraphics / ImageIO / Accelerate. /// - Returns: Resized image. func resizeWithScaleAspectFitMode(to dimension: CGFloat, resizeFramework: ResizeFramework = .coreGraphics) -> UIImage? { if max(size.width, size.height) <= dimension { return self } var newSize: CGSize! let aspectRatio = size.width/size.height if aspectRatio > 1 { // Landscape image newSize = CGSize(width: dimension, height: dimension / aspectRatio) } else { // Portrait image newSize = CGSize(width: dimension * aspectRatio, height: dimension) } return resize(to: newSize, with: resizeFramework) } /// Resize image from given size. /// /// - Parameter newSize: Size of the image output. /// - Parameter resizeFramework: Technique for image resizing: UIKit / CoreImage / CoreGraphics / ImageIO / Accelerate. /// - Returns: Resized image. public func resize(to newSize: CGSize, with resizeFramework: ResizeFramework = .coreGraphics) -> UIImage? { switch resizeFramework { case .uikit: return resizeWithUIKit(to: newSize) case .coreGraphics: return resizeWithCoreGraphics(to: newSize) case .coreImage: return resizeWithCoreImage(to: newSize) case .imageIO: return resizeWithImageIO(to: newSize) case .accelerate: return resizeWithAccelerate(to: newSize) } } // MARK: - UIKit /// Resize image from given size. /// /// - Parameter newSize: Size of the image output. /// - Returns: Resized image. private func resizeWithUIKit(to newSize: CGSize) -> UIImage? { UIGraphicsBeginImageContextWithOptions(newSize, true, 1.0) self.draw(in: CGRect(origin: .zero, size: newSize)) defer { UIGraphicsEndImageContext() } return UIGraphicsGetImageFromCurrentImageContext() } // MARK: - CoreImage /// Resize CI image from given size. /// /// - Parameter newSize: Size of the image output. /// - Returns: Resized image. // https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html private func resizeWithCoreImage(to newSize: CGSize) -> UIImage? { guard let cgImage = cgImage, let filter = CIFilter(name: "CILanczosScaleTransform") else { return nil } let ciImage = CIImage(cgImage: cgImage) let scale = (Double)(newSize.width) / (Double)(ciImage.extent.size.width) filter.setValue(ciImage, forKey: kCIInputImageKey) filter.setValue(NSNumber(value:scale), forKey: kCIInputScaleKey) filter.setValue(1.0, forKey: kCIInputAspectRatioKey) guard let outputImage = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil } let context = CIContext(options: [.useSoftwareRenderer: false]) guard let resultCGImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil } return UIImage(cgImage: resultCGImage) } // MARK: - CoreGraphics /// Resize image from given size. /// /// - Parameter newSize: Size of the image output. /// - Returns: Resized image. private func resizeWithCoreGraphics(to newSize: CGSize) -> UIImage? { guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil } let width = Int(newSize.width) let height = Int(newSize.height) let bitsPerComponent = cgImage.bitsPerComponent let bytesPerRow = cgImage.bytesPerRow let bitmapInfo = cgImage.bitmapInfo guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { return nil } context.interpolationQuality = .high let rect = CGRect(origin: CGPoint.zero, size: newSize) context.draw(cgImage, in: rect) return context.makeImage().flatMap { UIImage(cgImage: $0) } } // MARK: - ImageIO /// Resize image from given size. /// /// - Parameter newSize: Size of the image output. /// - Returns: Resized image. private func resizeWithImageIO(to newSize: CGSize) -> UIImage? { var resultImage = self guard let data = jpegData(compressionQuality: 1.0) else { return resultImage } let imageCFData = NSData(data: data) as CFData let options = [ kCGImageSourceCreateThumbnailWithTransform: true, kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceThumbnailMaxPixelSize: max(newSize.width, newSize.height) ] as CFDictionary guard let source = CGImageSourceCreateWithData(imageCFData, nil), let imageReference = CGImageSourceCreateThumbnailAtIndex(source, 0, options) else { return resultImage } resultImage = UIImage(cgImage: imageReference) return resultImage } // MARK: - Accelerate /// Resize image from given size. /// /// - Parameter newSize: Size of the image output. /// - Returns: Resized image. private func resizeWithAccelerate(to newSize: CGSize) -> UIImage? { var resultImage = self guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil } // create a source buffer var format = vImage_CGImageFormat(bitsPerComponent: numericCast(cgImage.bitsPerComponent), bitsPerPixel: numericCast(cgImage.bitsPerPixel), colorSpace: Unmanaged.passUnretained(colorSpace), bitmapInfo: cgImage.bitmapInfo, version: 0, decode: nil, renderingIntent: .absoluteColorimetric) var sourceBuffer = vImage_Buffer() defer { sourceBuffer.data.deallocate() } var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags)) guard error == kvImageNoError else { return resultImage } // create a destination buffer let destWidth = Int(newSize.width) let destHeight = Int(newSize.height) let bytesPerPixel = cgImage.bitsPerPixel let destBytesPerRow = destWidth * bytesPerPixel let destData = UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow) defer { destData.deallocate() } var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow) // scale the image error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling)) guard error == kvImageNoError else { return resultImage } // create a CGImage from vImage_Buffer let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue() guard error == kvImageNoError else { return resultImage } // create a UIImage if let scaledImage = destCGImage.flatMap({ UIImage(cgImage: $0) }) { resultImage = scaledImage } return resultImage } } Usage Get image size import UIKit // https://stackoverflow.com/a/55765409/4488252 extension UIImage { func getFileSizeInfo(allowedUnits: ByteCountFormatter.Units = .useMB, countStyle: ByteCountFormatter.CountStyle = .memory, compressionQuality: CGFloat = 1.0) -> String? { // https://developer.apple.com/documentation/foundation/bytecountformatter let formatter = ByteCountFormatter() formatter.allowedUnits = allowedUnits formatter.countStyle = countStyle return getSizeInfo(formatter: formatter, compressionQuality: compressionQuality) } func getSizeInfo(formatter: ByteCountFormatter, compressionQuality: CGFloat = 1.0) -> String? { guard let imageData = jpegData(compressionQuality: compressionQuality) else { return nil } return formatter.string(fromByteCount: Int64(imageData.count)) } } Test function private func test() { guard let img = UIImage(named: "img") else { return } printInfo(of: img, title: "original image |") let dimension: CGFloat = 2000 var framework: UIImage.ResizeFramework = .accelerate var startTime = Date() if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) { printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime) } framework = .coreGraphics startTime = Date() if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) { printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime) } framework = .coreImage startTime = Date() if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) { printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime) } framework = .imageIO startTime = Date() if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) { printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime) } framework = .uikit startTime = Date() if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) { printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime) } } private func printInfo(of image: UIImage, title: String, with resizeFramework: UIImage.ResizeFramework? = nil, startedTime: Date? = nil) { var description = "\(title) \(image.size)" if let startedTime = startedTime { description += ", execution time: \(Date().timeIntervalSince(startedTime))" } if let fileSize = image.getFileSizeInfo(compressionQuality: 0.9) { description += ", size: \(fileSize)" } if let resizeFramework = resizeFramework { description += ", framework: \(resizeFramework)" } print(description) } Output original image | (5790.0, 8687.0), size: 17.1 MB resized image | (1333.0, 2000.0), execution time: 0.8192930221557617, size: 1.1 MB, framework: accelerate resized image | (1333.0, 2000.0), execution time: 0.44696998596191406, size: 1 MB, framework: coreGraphics resized image | (1334.0, 2000.0), execution time: 54.172922015190125, size: 1.1 MB, framework: coreImage resized image | (1333.0, 2000.0), execution time: 1.8765920400619507, size: 1.1 MB, framework: imageIO resized image | (1334.0, 2000.0), execution time: 0.4638739824295044, size: 1 MB, framework: uikit
For Swift 4.0 and iOS 10 extension UIImage { func resizeImage(_ dimension: CGFloat, opaque: Bool, contentMode: UIViewContentMode = .scaleAspectFit) -> UIImage { var width: CGFloat var height: CGFloat var newImage: UIImage let size = self.size let aspectRatio = size.width/size.height switch contentMode { case .scaleAspectFit: if aspectRatio > 1 { // Landscape image width = dimension height = dimension / aspectRatio } else { // Portrait image height = dimension width = dimension * aspectRatio } default: fatalError("UIIMage.resizeToFit(): FATAL: Unimplemented ContentMode") } if #available(iOS 10.0, *) { let renderFormat = UIGraphicsImageRendererFormat.default() renderFormat.opaque = opaque let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height), format: renderFormat) newImage = renderer.image { (context) in self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) } } else { UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), opaque, 0) self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) newImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() } return newImage } }
Since #KiritModi 's answer is from 2015, this is the Swift 3.0's version: func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage { let size = image.size let widthRatio = targetSize.width / image.size.width let heightRatio = targetSize.height / image.size.height // Figure out what our orientation is, and use that to form the rectangle var newSize: CGSize if(widthRatio > heightRatio) { newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) } else { newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) } // This is the rect that we've calculated out and this is what is actually used below let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) // Actually do the resizing to the rect using the ImageContext stuff UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! }
For Swift 5.0 and iOS 12 extension UIImage { func imageResized(to size: CGSize) -> UIImage { return UIGraphicsImageRenderer(size: size).image { _ in draw(in: CGRect(origin: .zero, size: size)) } } } use: let image = #imageLiteral(resourceName: "ic_search") cell!.search.image = image.imageResized(to: cell!.search.frame.size)
For Swift 4 I would just make an extension on UIImage with referencing to self. import UIKit extension UIImage { func resizeImage(targetSize: CGSize) -> UIImage { let size = self.size let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height let newSize = widthRatio > heightRatio ? CGSize(width: size.width * heightRatio, height: size.height * heightRatio) : CGSize(width: size.width * widthRatio, height: size.height * widthRatio) let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) self.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } }
It's also possible to use AlamofireImage (https://github.com/Alamofire/AlamofireImage) let size = CGSize(width: 30.0, height: 30.0) let aspectScaledToFitImage = image.af_imageAspectScaled(toFit: size) The function in the previous post gave me a blurry result.
Swift 3 Version and Extension style This answer come from #Kirit Modi. extension UIImage { func resizeImage(targetSize: CGSize) -> UIImage { let size = self.size let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height // Figure out what our orientation is, and use that to form the rectangle var newSize: CGSize if(widthRatio > heightRatio) { newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) } else { newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) } // This is the rect that we've calculated out and this is what is actually used below let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) // Actually do the resizing to the rect using the ImageContext stuff UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) self.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } }
Updated Swift 5 version that uses the latest UIGraphicsImageRenderer API. extension UIImage { public func resized(to target: CGSize) -> UIImage { let ratio = min( target.height / size.height, target.width / size.width ) let new = CGSize( width: size.width * ratio, height: size.height * ratio ) let renderer = UIGraphicsImageRenderer(size: new) return renderer.image { _ in self.draw(in: CGRect(origin: .zero, size: new)) } } }
Swift 4, extension version, NO WHITE LINE ON EDGES. Nobody seems to be mentioning that if image.draw() is called with non-integer values, resulting image could show a white line artifact at the right or bottom edge. extension UIImage { func scaled(with scale: CGFloat) -> UIImage? { // size has to be integer, otherwise it could get white lines let size = CGSize(width: floor(self.size.width * scale), height: floor(self.size.height * scale)) UIGraphicsBeginImageContext(size) draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image }
Swift 5 version respecting ratio (scaleToFill) and centering image: extension UIImage { func resized(to newSize: CGSize) -> UIImage { return UIGraphicsImageRenderer(size: newSize).image { _ in let hScale = newSize.height / size.height let vScale = newSize.width / size.width let scale = max(hScale, vScale) // scaleToFill let resizeSize = CGSize(width: size.width*scale, height: size.height*scale) var middle = CGPoint.zero if resizeSize.width > newSize.width { middle.x -= (resizeSize.width-newSize.width)/2.0 } if resizeSize.height > newSize.height { middle.y -= (resizeSize.height-newSize.height)/2.0 } draw(in: CGRect(origin: middle, size: resizeSize)) } } }
Swift 4 Version extension UIImage { func resizeImage(_ newSize: CGSize) -> UIImage? { func isSameSize(_ newSize: CGSize) -> Bool { return size == newSize } func scaleImage(_ newSize: CGSize) -> UIImage? { func getScaledRect(_ newSize: CGSize) -> CGRect { let ratio = max(newSize.width / size.width, newSize.height / size.height) let width = size.width * ratio let height = size.height * ratio return CGRect(x: 0, y: 0, width: width, height: height) } func _scaleImage(_ scaledRect: CGRect) -> UIImage? { UIGraphicsBeginImageContextWithOptions(scaledRect.size, false, 0.0); draw(in: scaledRect) let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage() UIGraphicsEndImageContext() return image } return _scaleImage(getScaledRect(newSize)) } return isSameSize(newSize) ? self : scaleImage(newSize)! } }
UIImage Extension Swift 5 extension UIImage { func resize(_ width: CGFloat, _ height:CGFloat) -> UIImage? { let widthRatio = width / size.width let heightRatio = height / size.height let ratio = widthRatio > heightRatio ? heightRatio : widthRatio let newSize = CGSize(width: size.width * ratio, height: size.height * ratio) let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) self.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } } Use : UIImage().resize(200, 300)
All of the listed answers so far seem to result in an image of a reduced size, however the size isn't measured in pixels. Here's a Swift 5, pixel-based resize. extension UIImage { func resize(_ max_size: CGFloat) -> UIImage { // adjust for device pixel density let max_size_pixels = max_size / UIScreen.main.scale // work out aspect ratio let aspectRatio = size.width/size.height // variables for storing calculated data var width: CGFloat var height: CGFloat var newImage: UIImage if aspectRatio > 1 { // landscape width = max_size_pixels height = max_size_pixels / aspectRatio } else { // portrait height = max_size_pixels width = max_size_pixels * aspectRatio } // create an image renderer of the correct size let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height), format: UIGraphicsImageRendererFormat.default()) // render the image newImage = renderer.image { (context) in self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) } // return the image return newImage } } Usage: image.resize(500)
Here's a general method (in Swift 5) for downscaling an image to fit a size. The resulting image can have the same aspect ratio as the original, or it can be the target size with the original image centered in it. If the image is smaller than the target size, it is not resized. extension UIImage { func scaledDown(into size:CGSize, centered:Bool = false) -> UIImage { var (targetWidth, targetHeight) = (self.size.width, self.size.height) var (scaleW, scaleH) = (1 as CGFloat, 1 as CGFloat) if targetWidth > size.width { scaleW = size.width/targetWidth } if targetHeight > size.height { scaleH = size.height/targetHeight } let scale = min(scaleW,scaleH) targetWidth *= scale; targetHeight *= scale let sz = CGSize(width:targetWidth, height:targetHeight) if !centered { return UIGraphicsImageRenderer(size:sz).image { _ in self.draw(in:CGRect(origin:.zero, size:sz)) } } let x = (size.width - targetWidth)/2 let y = (size.height - targetHeight)/2 let origin = CGPoint(x:x,y:y) return UIGraphicsImageRenderer(size:size).image { _ in self.draw(in:CGRect(origin:origin, size:sz)) } } }
Swift 4 Solution- Use this function func image(with image: UIImage, scaledTo newSize: CGSize) -> UIImage { UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() drawingImageView.image = newImage return newImage ?? UIImage() } Calling a function:- image(with: predictionImage, scaledTo: CGSize(width: 28.0, height: 28.0) here 28.0 is the pixel size that you want to set
Swift 4.2 version of #KiritModi answer func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage { let size = image.size let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height var newSize: CGSize if(widthRatio > heightRatio) { newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) } else { newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) } let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! }
calling of resizeimage method let image1 = resizeimage(image: myimage.image!, withSize: CGSize(width:200, height: 200)) method for resizeing image func resizeimage(image:UIImage,withSize:CGSize) -> UIImage { var actualHeight:CGFloat = image.size.height var actualWidth:CGFloat = image.size.width let maxHeight:CGFloat = withSize.height let maxWidth:CGFloat = withSize.width var imgRatio:CGFloat = actualWidth/actualHeight let maxRatio:CGFloat = maxWidth/maxHeight let compressionQuality = 0.5 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 rec:CGRect = CGRect(x:0.0,y:0.0,width:actualWidth,height:actualHeight) UIGraphicsBeginImageContext(rec.size) image.draw(in: rec) let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()! let imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality)) UIGraphicsEndImageContext() let resizedimage = UIImage(data: imageData!) return resizedimage! }
Here you have two simple functions of UIImage extension: func scaledWithMaxWidthOrHeightValue(value: CGFloat) -> UIImage? { let width = self.size.width let height = self.size.height let ratio = width/height var newWidth = value var newHeight = value if ratio > 1 { newWidth = width * (newHeight/height) } else { newHeight = height * (newWidth/width) } UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0) draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } func scaled(withScale scale: CGFloat) -> UIImage? { let size = CGSize(width: self.size.width * scale, height: self.size.height * scale) UIGraphicsBeginImageContextWithOptions(size, false, 0) draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image }
SWIFT 5 - XCODE 12 -- RESIZE IMAGE & No White line I used a wonderful solution above for Swift 5. And I changed one bit to include the term "floor" as I was getting a white line around my resized images. This rounds it to the nearest pixel or something so it looks great! I also had to change the syntax around the image name when the function is called (last line). //method for resizing image func resizeimage(image:UIImage,withSize:CGSize) -> UIImage { var actualHeight:CGFloat = image.size.height var actualWidth:CGFloat = image.size.width let maxHeight:CGFloat = withSize.height let maxWidth:CGFloat = withSize.width var imgRatio:CGFloat = actualWidth/actualHeight let maxRatio:CGFloat = maxWidth/maxHeight let compressionQuality = 0.5 if (actualHeight>maxHeight||actualWidth>maxWidth) { if (imgRatio<maxRatio){ //adjust width according to maxHeight imgRatio = maxHeight/actualHeight actualWidth = floor(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 rec:CGRect = CGRect(x:0.0,y:0.0,width:actualWidth,height:actualHeight) UIGraphicsBeginImageContext(rec.size) image.draw(in: rec) let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()! let imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality)) UIGraphicsEndImageContext() let resizedimage = UIImage(data: imageData!) return resizedimage! } //calling of resizeimage method: let myimage = UIImage(named: "imagename") let image1 = resizeimage(image: myimage!, withSize: CGSize(width:50, height: 50)).withRenderingMode(.alwaysOriginal)
Example is for image minimize to 1024 and less func resizeImage(image: UIImage) -> UIImage { if image.size.height >= 1024 && image.size.width >= 1024 { UIGraphicsBeginImageContext(CGSize(width:1024, height:1024)) image.draw(in: CGRect(x:0, y:0, width:1024, height:1024)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } else if image.size.height >= 1024 && image.size.width < 1024 { UIGraphicsBeginImageContext(CGSize(width:image.size.width, height:1024)) image.draw(in: CGRect(x:0, y:0, width:image.size.width, height:1024)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } else if image.size.width >= 1024 && image.size.height < 1024 { UIGraphicsBeginImageContext(CGSize(width:1024, height:image.size.height)) image.draw(in: CGRect(x:0, y:0, width:1024, height:image.size.height)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } else { return image } }
You can use this for fit image at Swift 3; extension UIImage { func resizedImage(newSize: CGSize) -> UIImage { // Guard newSize is different guard self.size != newSize else { return self } UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0); self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)) let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return newImage } 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 } } Usage; let resizedImage = image.resizedImageWithinRect(rectSize: CGSize(width: 1900, height: 1900))
UIImage aspect fill
I'm trying to perform aspect fill on UIImage (with reusable extension), but I've only got this far: extension UIImage { func resizeToCircleImage(targetSize: CGSize, contentMode: UIViewContentMode) -> UIImage { UIGraphicsBeginImageContextWithOptions(targetSize, true, 0.0) let rect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height) self.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } } So, this is for an MKAnnotationView. It end up looking like this: But I wanted to aspect fit this image: https://en.wikipedia.org/wiki/Panorama#/media/File:Panorama_of_the_courtyard_of_the_Great_Mosque_of_Kairouan.jpg
Considering a category of UIImage, - (UIImage *)aspectFillToSize:(CGSize)size { CGFloat imgAspect = self.size.width / self.size.height; CGFloat sizeAspect = size.width/size.height; CGSize scaledSize; if (sizeAspect > imgAspect) { // increase width, crop height scaledSize = CGSizeMake(size.width, size.width / imgAspect); } else { // increase height, crop width scaledSize = CGSizeMake(size.height * imgAspect, size.height); } UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height)); [self drawInRect:CGRectMake(0.0f, 0.0f, scaledSize.width, scaledSize.height)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
Swift extension version of code from "karim": private extension UIImage { func resize(to newSize: CGSize) -> UIImage? { guard size != newSize else { return self } let imgAspect = size.width/size.height let sizeAspect = newSize.width/newSize.height let scaledSize: CGSize if sizeAspect > imgAspect { scaledSize = CGSize(width: newSize.width, height: newSize.width/imgAspect) } else { scaledSize = CGSize(width: newSize.height * imgAspect, height: newSize.height) } UIGraphicsBeginImageContextWithOptions(newSize, true, 1.0) defer { UIGraphicsEndImageContext() } let context = UIGraphicsGetCurrentContext() guard let context = context else { return nil } context.clip(to: CGRect(origin: .zero, size: newSize)) draw(in: CGRect(origin: .zero, size: scaledSize)) return UIGraphicsGetImageFromCurrentImageContext() } }
Getting a resized image from an UIImageView
I need to upload image data to my Parse.com backend, but I found out that my pictures are way too big, dimensions wise. NSHipster had an article on image resizing with this code: import ImageIO if let imageSource = CGImageSourceCreateWithURL(self.URL, nil) { let options: CFDictionary = [ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0, kCGImageSourceCreateThumbnailFromImageIfAbsent: true ] let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options).flatMap { UIImage(CGImage: $0) } } Unfortunately, this code does not compile in Swift 1.2. In particular there are 2 problems. The CFDictionary could not be initialized, and flatMap method was not recognized.
Here is some functions for resize your image: import UIKit func RBSquareImageTo(image: UIImage, size: CGSize) -> UIImage { return RBResizeImage(RBSquareImage(image), size) } func RBSquareImage(image: UIImage) -> UIImage { var originalWidth = image.size.width var originalHeight = image.size.height var edge: CGFloat if originalWidth > originalHeight { edge = originalHeight } else { edge = originalWidth } var posX = (originalWidth - edge) / 2.0 var posY = (originalHeight - edge) / 2.0 var cropSquare = CGRectMake(posX, posY, edge, edge) var imageRef = CGImageCreateWithImageInRect(image.CGImage, cropSquare); return UIImage(CGImage: imageRef, scale: UIScreen.mainScreen().scale, orientation: image.imageOrientation) } func RBResizeImage(image: UIImage, targetSize: CGSize) -> UIImage { let size = image.size let widthRatio = targetSize.width / image.size.width let heightRatio = targetSize.height / image.size.height // Figure out what our orientation is, and use that to form the rectangle var newSize: CGSize if(widthRatio > heightRatio) { newSize = CGSizeMake(size.width * heightRatio, size.height * heightRatio) } else { newSize = CGSizeMake(size.width * widthRatio, size.height * widthRatio) } // This is the rect that we've calculated out and this is what is actually used below let rect = CGRectMake(0, 0, newSize.width, newSize.height) // Actually do the resizing to the rect using the ImageContext stuff UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.drawInRect(rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } From HERE.
Here is the Objective C code that scale the image given by parameters. -(UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize; { UIGraphicsBeginImageContext( newSize ); [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)]; UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; }
Add transparent space around a UIImage
Lets say we have an image of 600X400 pixel and we want to end up with an new image of 1000x1000 pixel which contains the initial image in the centre and transparent space around it. How can I achieve that in code?
In Swift you can write an extension to UIImage that draws image with insets around it. Swift 3: import UIKit extension UIImage { func imageWithInsets(insets: UIEdgeInsets) -> UIImage? { UIGraphicsBeginImageContextWithOptions( CGSize(width: self.size.width + insets.left + insets.right, height: self.size.height + insets.top + insets.bottom), false, self.scale) let _ = UIGraphicsGetCurrentContext() let origin = CGPoint(x: insets.left, y: insets.top) self.draw(at: origin) let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageWithInsets } } OLD ANSWER: import UIKit extension UIImage { func imageWithInsets(insets: UIEdgeInsets) -> UIImage { UIGraphicsBeginImageContextWithOptions( CGSizeMake(self.size.width + insets.left + insets.right, self.size.height + insets.top + insets.bottom), false, self.scale) let context = UIGraphicsGetCurrentContext() let origin = CGPoint(x: insets.left, y: insets.top) self.drawAtPoint(origin) let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageWithInsets } }
This is the solution in Swift 4 inspired by DrummerB answer: import UIKit extension UIImage { func addImagePadding(x: CGFloat, y: CGFloat) -> UIImage? { let width: CGFloat = size.width + x let height: CGFloat = size.height + y UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0) let origin: CGPoint = CGPoint(x: (width - size.width) / 2, y: (height - size.height) / 2) draw(at: origin) let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageWithPadding } } How to apply: let image = UIImage(named: "your-image")! let imageView = UIImageView(image: image.addImagePadding(x: 50, y: 50)) imageView.backgroundColor = UIColor.gray view.addSubview(imageView) Features: Simply pass padding values via parameters Colored padding (by setting the UIGraphicsBeginImageContextWithOptions opaque parameter to false)
You create a new image context that is 1000x1000, draw your old image in the middle, then get the new UIImage from the context. // Setup a new context with the correct size CGFloat width = 1000; CGFloat height = 1000; UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); UIGraphicsPushContext(context); // Now we can draw anything we want into this new context. CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f, (height - oldImage.size.height) / 2.0f); [oldImage drawAtPoint:origin]; // Clean up and get the new image. UIGraphicsPopContext(); UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
A fix for appsunited's answer with better naming convension. To not confuse it the function is mutating or not: extension UIImage { func withPadding(_ padding: CGFloat) -> UIImage? { return withPadding(x: padding, y: padding) } func withPadding(x: CGFloat, y: CGFloat) -> UIImage? { let newWidth = size.width + 2 * x let newHeight = size.height + 2 * y let newSize = CGSize(width: newWidth, height: newHeight) UIGraphicsBeginImageContextWithOptions(newSize, false, 0) let origin = CGPoint(x: (newWidth - size.width) / 2, y: (newHeight - size.height) / 2) draw(at: origin) let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageWithPadding } }
Make a category on UIImage and try this: + (UIImage *)imageWithInsets:(CGRect)insetRect image:(UIImage *)image { CGRect newRect = CGRectMake(0.0, 0.0, insetRect.origin.x+insetRect.size.width+image.size.width, insetRect.origin.y+insetRect.size.height+image.size.height); // Setup a new context with the correct size UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); UIGraphicsPushContext(context); // Now we can draw anything we want into this new context. CGPoint origin = CGPointMake(insetRect.origin.x, insetRect.origin.y); [image drawAtPoint:origin]; // Clean up and get the new image. UIGraphicsPopContext(); UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; }
How to scale down a UIImage and make it crispy / sharp at the same time instead of blurry?
I need to scale down an image, but in a sharp way. In Photoshop for example there are the image size reduction options "Bicubic Smoother" (blurry) and "Bicubic Sharper". Is this image downscaling algorithm open sourced or documented somewhere or does the SDK offer methods to do this?
Merely using imageWithCGImage is not sufficient. It will scale, but the result will be blurry and suboptimal whether scaling up or down. If you want to get the aliasing right and get rid of the "jaggies" you need something like this: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/. My working test code looks something like this, which is Trevor's solution with one small adjustment to work with my transparent PNGs: - (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGImageRef imageRef = image.CGImage; UIGraphicsBeginImageContextWithOptions(newSize, NO, 0); CGContextRef context = UIGraphicsGetCurrentContext(); // Set the quality level to use when rescaling CGContextSetInterpolationQuality(context, kCGInterpolationHigh); CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height); CGContextConcatCTM(context, flipVertical); // Draw into the context; this scales the image CGContextDrawImage(context, newRect, imageRef); // Get the resized image from the context and a UIImage CGImageRef newImageRef = CGBitmapContextCreateImage(context); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; CGImageRelease(newImageRef); UIGraphicsEndImageContext(); return newImage; }
For those using Swift here is the accepted answer in Swift: func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) { let newRect = CGRectIntegral(CGRectMake(0,0, newSize.width, newSize.height)) let imageRef = image.CGImage UIGraphicsBeginImageContextWithOptions(newSize, false, 0) let context = UIGraphicsGetCurrentContext() // Set the quality level to use when rescaling CGContextSetInterpolationQuality(context, kCGInterpolationHigh) let flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height) CGContextConcatCTM(context, flipVertical) // Draw into the context; this scales the image CGContextDrawImage(context, newRect, imageRef) let newImageRef = CGBitmapContextCreateImage(context) as CGImage let newImage = UIImage(CGImage: newImageRef) // Get the resized image from the context and a UIImage UIGraphicsEndImageContext() return newImage }
If someone is looking for Swift version, here is the Swift version of #Dan Rosenstark's accepted answer: 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 }
If you retain the original aspect ratio of the image while scaling, you'll always end up with a sharp image no matter how much you scale down. You can use the following method for scaling: + (UIImage *)imageWithCGImage:(CGImageRef)imageRef scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
For Swift 3 func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) { let newRect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height).integral UIGraphicsBeginImageContextWithOptions(newSize, false, 0) let context = UIGraphicsGetCurrentContext() // Set the quality level to use when rescaling context!.interpolationQuality = CGInterpolationQuality.default let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newSize.height) context!.concatenate(flipVertical) // Draw into the context; this scales the image context?.draw(image.cgImage!, in: CGRect(x: 0.0,y: 0.0, width: newRect.width, height: newRect.height)) let newImageRef = context!.makeImage()! as CGImage let newImage = UIImage(cgImage: newImageRef) // Get the resized image from the context and a UIImage UIGraphicsEndImageContext() return newImage }
#YAR your solution is working properly. There is only one thing which does not fit my requirements: The whole image is resized. I wrote a Method which did it like the photos app on iphone. This calculates the "longer side" and cuts off the "overlay" resulting in getting much better results concerning the quality of the image. - (UIImage *)resizeImageProportionallyIntoNewSize:(CGSize)newSize; { CGFloat scaleWidth = 1.0f; CGFloat scaleHeight = 1.0f; if (CGSizeEqualToSize(self.size, newSize) == NO) { //calculate "the longer side" if(self.size.width > self.size.height) { scaleWidth = self.size.width / self.size.height; } else { scaleHeight = self.size.height / self.size.width; } } //prepare source and target image UIImage *sourceImage = self; UIImage *newImage = nil; // Now we create a context in newSize and draw the image out of the bounds of the context to get // A proportionally scaled image by cutting of the image overlay UIGraphicsBeginImageContext(newSize); //Center image point so that on each egde is a little cutoff CGRect thumbnailRect = CGRectZero; thumbnailRect.size.width = newSize.width * scaleWidth; thumbnailRect.size.height = newSize.height * scaleHeight; thumbnailRect.origin.x = (int) (newSize.width - thumbnailRect.size.width) * 0.5; thumbnailRect.origin.y = (int) (newSize.height - thumbnailRect.size.height) * 0.5; [sourceImage drawInRect:thumbnailRect]; newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); if(newImage == nil) NSLog(#"could not scale image"); return newImage ; }
For swift 4.2: extension UIImage { func resized(By coefficient:CGFloat) -> UIImage? { guard coefficient >= 0 && coefficient <= 1 else { print("The coefficient must be a floating point number between 0 and 1") return nil } let newWidth = size.width * coefficient let newHeight = size.height * coefficient UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight)) draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } }
This extension should scale the image while keeping original aspect ratio. The rest of the image is cropped. (Swift 3) extension UIImage { func thumbnail(ofSize proposedSize: CGSize) -> UIImage? { let scale = min(size.width/proposedSize.width, size.height/proposedSize.height) let newSize = CGSize(width: size.width/scale, height: size.height/scale) let newOrigin = CGPoint(x: (proposedSize.width - newSize.width)/2, y: (proposedSize.height - newSize.height)/2) let thumbRect = CGRect(origin: newOrigin, size: newSize).integral UIGraphicsBeginImageContextWithOptions(proposedSize, false, 0) draw(in: thumbRect) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result } }