How to downscale a UIImage in IOS by the Data size
Right now, you have a routine that says:
// Check if the image size is too large
if ((imageData.length/1024) >= 1024) {
while ((imageData.length/1024) >= 1024) {
NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024)));
// While the imageData is too large scale down the image
// Get the current image size
CGSize currentSize = CGSizeMake(image.size.width, image.size.height);
// Resize the image
image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality];
// Pass the NSData out again
imageData = UIImageJPEGRepresentation(image, kMESImageQuality);
}
}
I wouldn't advise recursively resizing the image. Every time you resize, you lose some quality (often manifesting itself as a "softening" of the image with loss of detail, with cumulative effects). You always want to go back to original image and resize that smaller and smaller. (As a minor aside, that if
statement is redundant, too.)
I might suggest the following:
NSData *imageData = UIImageJPEGRepresentation(image, kMESImageQuality);
double factor = 1.0;
double adjustment = 1.0 / sqrt(2.0); // or use 0.8 or whatever you want
CGSize size = image.size;
CGSize currentSize = size;
UIImage *currentImage = image;
while (imageData.length >= (1024 * 1024))
{
factor *= adjustment;
currentSize = CGSizeMake(roundf(size.width * factor), roundf(size.height * factor));
currentImage = [image resizedImage:currentSize interpolationQuality:kMESImageQuality];
imageData = UIImageJPEGRepresentation(currentImage, kMESImageQuality);
}
Note, I'm not touching image
, the original image, but rather assigning currentImage
by doing a resize from the original image each time, by a decreasing scale each time.
BTW, if you're wondering about my cryptic 1.0 / sqrt(2.0)
, I was trying to draw a compromise between your iterative 80% factor and my desire to favor resizing by a power of 2 where I can (because a reduction retains more sharpness when done by a power of 2). But use whatever adjustment
factor you want.
Finally, if you're doing this on huge images, you might think about using @autoreleasepool
blocks. You'll want to profile your app in Allocations in Instruments and see where your high water mark is, as in the absence of autorelease pools, this may constitute a fairly aggressive use of memory.
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)];
How do I resize the UIImage to reduce upload image size
Swift 5.4 & Xcode 13
I was not satisfied with the solutions here, which generate an image based on a given KB size, since most of them used .jpegData(compressionQuality: x)
. This method won't work with large images, since even with compression quality set to 0.0, the large image will remain large, e.g. a 10 MB produced by portrait mode of a newer iPhone still will be above 1 MB with compressionQuality set to 0.0.
Therefore I used some answers here and rewrote a Helper Struct which converts an image in a background que:
import UIKit
struct ImageCompressor {
static func compress(image: UIImage, maxByte: Int,
completion: @escaping (UIImage?) -> ()) {
DispatchQueue.global(qos: .userInitiated).async {
guard let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
var iterationImage: UIImage? = image
var iterationImageSize = currentImageSize
var iterationCompression: CGFloat = 1.0
while iterationImageSize > maxByte && iterationCompression > 0.01 {
let percantageDecrease = getPercantageToDecreaseTo(forDataCount: iterationImageSize)
let canvasSize = CGSize(width: image.size.width * iterationCompression,
height: image.size.height * iterationCompression)
UIGraphicsBeginImageContextWithOptions(canvasSize, false, image.scale)
defer { UIGraphicsEndImageContext() }
image.draw(in: CGRect(origin: .zero, size: canvasSize))
iterationImage = UIGraphicsGetImageFromCurrentImageContext()
guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
iterationImageSize = newImageSize
iterationCompression -= percantageDecrease
}
completion(iterationImage)
}
}
private static func getPercantageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
switch dataCount {
case 0..<3000000: return 0.05
case 3000000..<10000000: return 0.1
default: return 0.2
}
}
}
Compress an image to max 1 MB:
ImageCompressor.compress(image: image, maxByte: 1000000) { image in
guard let compressedImage = image else { return }
// Use compressedImage
}
}
Change NSData Size of a UIImage - Compress And Resize UIImage With a Maximum Megabyte Mb limit
Had to create my own function that compresses an image as small as it can get, and if it's still over my "max size" then it resizes, reserves and begins the compression iteration again. This does a fairly good job of getting the image as close as possible to target "max image file size" if it's over the file size. Also includes a failsafe after 1024 iterations. (Which should never last longer than a minute (but that's a scenario that would never occour... who uses images that are gigabytes on an iPhone? Haha))...
-(void)shrinkImage {
//IMPORTANT!!! THIS CODE WAS CREATED WITH "ARC" IN MIND... DO NOT USE WITHOUT ARC UNLESS YOU ALTER THIS CODE TO MANAGE MEMORY
float compressionVal = 1.0;
float maxVal = 9.7;//MB
UIImage *compressedImage = theUploadedImage.image; //get UIImage from imageView
int iterations = 0;
int totalIterations = 0;
float initialCompressionVal = 0.00000000f;
while (((((float)(UIImageJPEGRepresentation(compressedImage, compressionVal).length))/(1048576.000000000f)) > maxVal) && (totalIterations < 1024)) {
NSLog(@"Image is %f MB", (float)(((float)(UIImageJPEGRepresentation(compressedImage, compressionVal)).length)/(1048576.000000f)));//converts bytes to MB
compressionVal = (((compressionVal)+((compressionVal)*((float)(((float)maxVal)/((float)(((float)(UIImageJPEGRepresentation(compressedImage, compressionVal).length))/(1048576.000000000f)))))))/(2));
compressionVal *= 0.97;//subtracts 3% of it's current value just incase above algorithm limits at just above MaxVal and while loop becomes infinite.
if (initialCompressionVal == 0.00000000f) {
initialCompressionVal = compressionVal;
}
iterations ++;
if ((iterations >= 3) || (compressionVal < 0.1)) {
iterations = 0;
NSLog(@"%f", compressionVal);
compressionVal = 1.0f;
compressedImage = [UIImage imageWithData:UIImageJPEGRepresentation(compressedImage, compressionVal)];
float resizeAmount = 1.0f;
resizeAmount = (resizeAmount+initialCompressionVal)/(2);//percentage
resizeAmount *= 0.97;//3% boost just incase image compression algorithm reaches a limit.
resizeAmount = 1/(resizeAmount);//value
initialCompressionVal = 0.00000000f;
UIView *imageHolder = [[UIView alloc] initWithFrame:CGRectMake(0,0,(int)floorf((float)(compressedImage.size.width/(resizeAmount))), (int)floorf((float)(compressedImage.size.height/(resizeAmount))))];//round down to ensure frame isnt larger than image itself
UIImageView *theResizedImage = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,(int)ceilf((float)(compressedImage.size.width/(resizeAmount))), (int)ceilf((float)(compressedImage.size.height/(resizeAmount))))];//round up to ensure image fits
theResizedImage.image = compressedImage;
[imageHolder addSubview:theResizedImage];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(imageHolder.frame.size.width, imageHolder.frame.size.height), YES, 1.0f);
CGContextRef resize_context = UIGraphicsGetCurrentContext();
[imageHolder.layer renderInContext:resize_context];
compressedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//after 3 compressions, if we still haven't shrunk down to maxVal size, apply the maximum compression we can, then resize the image (90%?), then re-start the process, this time compressing the compressed version of the image we were checking.
NSLog(@"resize");
}
totalIterations ++;
}
if (totalIterations >= 1024) {
NSLog(@"Image was too big, gave up on trying to re-size");//too many iterations failsafe. Gave up on trying to resize.
} else {
NSData *imageData = UIImageJPEGRepresentation(compressedImage, compressionVal);
NSLog(@"FINAL Image is %f MB ... iterations: %i", (float)(((float)imageData.length)/(1048576.000000f)), totalIterations);//converts bytes to MB
theUploadedImage.image = [UIImage imageWithData:imageData];//save new image to UIImageView.
}
}
UIImage resizing scale
For Swift 4 And Swift 3 use below UIImage
extension for resizing image. It's calculated height according to given width.
extension UIImage {
func resized(toWidth width: CGFloat) -> UIImage? {
let canvasSize = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(origin: .zero, size: canvasSize))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
How to resize an UIImage based on device size and scale?
Since the scale factor is specified in UIGraphicsBeginImageContextWithOptions, it is not needed to multiply the width and height by the screen scale when scaledRect is calculated.
And If you specify a value of 0.0 in UIGraphicsBeginImageContextWithOptions, the scale factor is automatically set to the scale factor of the device’s main screen.
- (UIImage *)hResizeImage:(UIImage *)image withTargetSize:(CGSize)targetSize {
CGSize size = image.size;
CGFloat widthRatio = targetSize.width / image.size.width;
CGFloat heightRatio = targetSize.height / image.size.height;
// Figure out what our orientation is, and use that to form the rectangle
CGSize newSize;
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
CGRect scaledRect = CGRectMake(0, 0, newSize.width, newSize.height);
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(scaledRect.size, false, 0.0f);
[image drawInRect:scaledRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Reduce UIImage size to a manageable size (reduce bytes)
If you wish to simply compress your UIImage, you can use
NSData *dataForPNGFile = UIImagePNGRepresentation(yourImage);
to generate an NSData version of your image encoded as a PNG (easily inserted into an NSDictionary or written to disk), or you can use
NSData *dataForPNGFile = UIImageJPEGRepresentation(yourImage, 0.9f);
to do the same, only in a JPEG format. The second parameter is the image quality of the JPEG. Both of these should produce images that are smaller, memory-wise, than your UIImage.
Resizing a UIImage to create a smaller thumbnail (pixels-wise) using published methods is a little trickier. _imageScaledToSize is from the private API, and I'd highly recommend you not use it. For a means that works within the documented methods, see this post.
How to resize UIImageView based on UIImage's size/ratio in Swift 3?
It looks like you want to resize an ImageView according to the image ratio and the container view's size, here is the example in Swift (Sorry,the former answer with a bug, I fixed it):
let containerView = UIView(frame: CGRect(x:0,y:0,width:320,height:500))
let imageView = UIImageView()
if let image = UIImage(named: "a_image") {
let ratio = image.size.width / image.size.height
if containerView.frame.width > containerView.frame.height {
let newHeight = containerView.frame.width / ratio
imageView.frame.size = CGSize(width: containerView.frame.width, height: newHeight)
}
else{
let newWidth = containerView.frame.height * ratio
imageView.frame.size = CGSize(width: newWidth, height: containerView.frame.height)
}
}
Related Topics
Error Appstore Connect:Missing Purpose String in Info.Plist File
How to Get Selected Value from Uipickerview
Align Button Image to Right Edge of Uibutton
Tableview Reloaddata VS. Beginupdates & Endupdates
How to Cache Resources Loaded in an iPhone Uiwebview
How to Convert an iPhone Osstatus Code to Something Useful
Libmobilegestalt Mobilegestaltsupport.M:153 Mobilegestalt.C:550 Xcode Console
Url Scheme "Open Settings" iOS
Ios: Differencebetween -Init and -Viewload of a Viewcontroller
How to Check Whether iPhone and Apple Watch Are Connected
The Identity Used to Sign the Executable Is No Longer Valid
"Initialize" Class Method for Classes in Swift
Uiview Atop the Keyboard Similar to Imessage App
Uiviewcontrollerhierarchyinconsistency When Trying to Present a Modal View Controller
Create and Store PDF Document Programmatically Using Swift for iOS
Xcode Is Creating Generic Xcode Archive Instead of iOS App Archive
Modulename-Swift.H File Not Found in Xcode8
"'Cdvplugin.H' File Not Found" in Cordova as Component (Cleaver)