How to crop image view given a rect - ios

I want to display the crop view on uiimagepickercontrollers overlayView, and then crop the image with respect to the overlayImage rect. How can I calculate crop image given the rect of overlayView?

You can do it by using below chunk of code.
let imageRef:CGImage = uncroppedImage.cgImage!.cropping(to: bounds)!
let croppedImage:UIImage = UIImage(cgImage: imageRef)
Here you can pass your rect according to your requirement.
Also you can do it as as per apple suggestion.
Please find Official documentatation.
func cropImage(_ inputImage: UIImage, toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage?
{
let imageViewScale = max(inputImage.size.width / viewWidth,
inputImage.size.height / viewHeight)
// Scale cropRect to handle images larger than shown-on-screen size
let cropZone = CGRect(x:cropRect.origin.x * imageViewScale,
y:cropRect.origin.y * imageViewScale,
width:cropRect.size.width * imageViewScale,
height:cropRect.size.height * imageViewScale)
// Perform cropping in Core Graphics
guard let cutImageRef: CGImage = inputImage.cgImage?.cropping(to:cropZone)
else {
return nil
}
// Return image to UIImage
let croppedImage: UIImage = UIImage(cgImage: cutImageRef)
return croppedImage
}

private func CropImage( image:UIImage , cropRect:CGRect) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(cropRect.size, false, 0);
let context = UIGraphicsGetCurrentContext();
context?.translateBy(x: 0.0, y: image.size.height);
context?.scaleBy(x: 1.0, y: -1.0);
context?.draw(image.cgImage!, in: CGRect(x:0, y:0, width:image.size.width, height:image.size.height), byTiling: false);
context?.clip(to: [cropRect]);
let croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage!;
}

Related

How to crop a UIImage without losing it's scale property?

Goal: Crop a UIImage (that starts with a scale property of 2.0)
I perform the following code:
let croppedCGImage = originalUIImage.cgImage!.cropping(to: cropRect)
let croppedUIImage = UIImage(cgImage: croppedCGImage!)
This code works, however the result, croppedUIImage, has an incorrect scale property of 1.0.
I've tried specifying the scale when creating the final image:
let croppedUIImage = UIImage(cgImage: croppedCGImage!, scale: 2.0, orientation: .up)
This yields the correct scale, but it cuts the size dimensions in half incorrectly.
What should I do here?
(*Note: the scale property on the UIImage is important because I later save the image with UIImagePNGRepresentation(_ image: UIImage) which is affected by the scale property)
Edit:
I got the following to work. Unfortunately it's just substantially slower than the CGImage cropping function.
extension UIImage {
func cropping(to rect: CGRect) -> UIImage {
UIGraphicsBeginImageContextWithOptions(rect.size, false, self.scale)
self.draw(in: CGRect(x: -rect.origin.x, y: -rect.origin.y, width: self.size.width, height: self.size.height))
let croppedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return croppedImage
}
}
Try this:
extension UIImage {
func imageByCropToRect(rect:CGRect, scale:Bool) -> UIImage {
var rect = rect
var scaleFactor: CGFloat = 1.0
if scale {
scaleFactor = self.scale
rect.origin.x *= scaleFactor
rect.origin.y *= scaleFactor
rect.size.width *= scaleFactor
rect.size.height *= scaleFactor
}
var image: UIImage? = nil;
if rect.size.width > 0 && rect.size.height > 0 {
let imageRef = self.cgImage!.cropping(to: rect)
image = UIImage(cgImage: imageRef!, scale: scaleFactor, orientation: self.imageOrientation)
}
return image!
}
}
Use this Extension :-
extension UIImage {
func cropping(to quality: CGInterpolationQuality, rect: CGRect) -> UIImage {
UIGraphicsBeginImageContextWithOptions(rect.size, false, self.scale)
let context = UIGraphicsGetCurrentContext()! as CGContext
context.interpolationQuality = quality
let drawRect : CGRect = CGRect(x: -rect.origin.x, y: -rect.origin.y, width: self.size.width, height: self.size.height)
context.clip(to: CGRect(x:0, y:0, width: rect.size.width, height: rect.size.height))
self.draw(in: drawRect)
let croppedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return croppedImage
}
}
I'm using the ImageHelper Pod for iOS and tvOS and it's working perfectly and might also fit your needs.
It brings a lot UIImage Extensions such as:
Crop and Resize
// Crops an image to a new rect
func crop(bounds: CGRect) -> UIImage?
// Crops an image to a centered square
func cropToSquare() -> UIImage? {
// Resizes an image
func resize(size:CGSize, contentMode: UIImageContentMode = .ScaleToFill) -> UIImage?
Screen Density
// To create an image that is Retina aware, use the screen scale as a multiplier for your size. You should also use this technique for padding or borders.
let width = 140 * UIScreen.mainScreen().scale
let height = 140 * UIScreen.mainScreen().scale
let image = UIImage(named: "myImage")?.resize(CGSize(width: width, height: height))
Also stuff like: Image Effects
// Applies a light blur effect to the image
func applyLightEffect() -> UIImage?
// Applies a extra light blur effect to the image
func applyExtraLightEffect() -> UIImage?
// Applies a dark blur effect to the image
func applyDarkEffect() -> UIImage?
// Applies a color tint to an image
func applyTintEffect(tintColor: UIColor) -> UIImage?
// Applies a blur to an image based on the specified radius, tint color saturation and mask image
func applyBlur(blurRadius:CGFloat, tintColor:UIColor?, saturationDeltaFactor:CGFloat, maskImage:UIImage? = nil) -> UIImage?
-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
CGImageRelease(subImage);
return croppedImage;
}
calling
UIImage *imageSample=image;
CGRect rectMake1=CGRectMake(0, 0,imageSample.size.width*1/4, imageSample.size.height);
UIImage *img1=[[JRGlobal sharedInstance] getNeedImageFrom:imageSample cropRect:rectMake1];

Create a universal vector image programmatically

We can now create universal vector images in Xcode by adding them into image assets folder and then changing the scale factors to single vector. I would like to know how to do this programmatically. I've tried the following but no luck :(
func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) {
let newRect = CGRectIntegral(CGRectMake(0,0, newSize.width, newSize.height))
let imageRef = image.CGImage
let scaleFactor = UIScreen.mainScreen().scale
UIGraphicsBeginImageContextWithOptions(newSize, false, scaleFactor)
// UIGraphicsBeginImageContextWithOptions(newSize, false, 0.5)
let context = UIGraphicsGetCurrentContext()
// Set the quality level to use when rescaling
CGContextSetInterpolationQuality(context, CGInterpolationQuality.High)
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
}
This should do it. Yea...Apple kind of gears this towards setting it in storyboard by ticking the preserve vector data. I'm not sure how to preserve vector data programmatically.
public class var add: UIImage {
let path = bundle.url(forResource: "plusReal", withExtension: "pdf")
return drawPDFFromURL(url: path!)!
}
class func drawPDFFromURL(url: URL) -> UIImage? {
guard let document = CGPDFDocument(url as CFURL),
let page = document.page(at: 1) else {return nil }
let pageRect = page.getBoxRect(.mediaBox)
let renderer = UIGraphicsImageRenderer(size: pageRect.size)
let img = renderer.image { ctx in
UIColor.white.set()
ctx.fill(pageRect)
ctx.cgContext.translateBy(x: 0.0, y: pageRect.size.height)
ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
ctx.cgContext.drawPDFPage(page)
}
return img
}

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;
}

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
}
}

The simplest way to resize an UIImage?

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!
}

Resources