UIImage resizing not working properly
So I've been trying to figure out what I'm doing wrong for a while now and I can't figure it out. What I'm trying to accomplish is this:
- Take a photo with
UIImagePickerController
- Take the resulting photo and crop off the top and bottom so it becomes a square (similar to Instagram)
- Display that image within a
UIButton
For some reason, every time I take the photo it ends up distorted within the UIButton
and it appears as though the cropping didn't work properly. Here's what I do. Within the didFinishPickingMediaWithInfo
method I have the following code:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//Copy the image to the userImage variable
[picker dismissModalViewControllerAnimated:YES];
userImage = nil;
//Rotate & resize image
userImage = [self resizeAndRotatePhoto:(UIImage *)[info objectForKey:UIImagePickerControllerOriginalImage]];
NSLog(@"Userimage size, width: %f , height: %f", userImage.size.width , userImage.size.height);
//Update the image form field with the image and set the image in the controller
NSLog(@"Button size is height: %f , width: %f" , userImageAvatarButton.frame.size.height , userImageAvatarButton.frame.size.width);
[userImageAvatarButton.layer setMasksToBounds:YES];
[userImageAvatarButton.layer setCornerRadius:3.0];
[userImageAvatarButton setImage:userImage forState:UIControlStateNormal];
}
I'll include the resizeAndRotatePhoto method momentarily but the result is below. Also, in the code above, @property (strong) (UIImage *)userImage;
is defined in the ViewController's header file. The log output also results in:
2012-05-07 17:38:07.995 NewApp[10666:707] Userimage size, width: 1936.000000 , height: 1936.000000
2012-05-07 17:38:08.000 NewApp[10666:707] Button size is height: 60.000000 , width: 60.000000
As you see in the image below, it ends up distorted.
As for the resizeAndRotate method, here it is:
- (UIImage *)resizeAndRotatePhoto:(UIImage *)source
{
if( source.imageOrientation == UIImageOrientationRight )
{
source = [self rotateImage:source byDegrees:90];
}
if( userImage.imageOrientation == UIImageOrientationLeft)
{
source = [self rotateImage:source byDegrees:-90];
}
CGFloat x,y;
CGFloat size;
if( source.size.width > source.size.height ){
size = source.size.height;
x = (source.size.width - source.size.height)/2;
y = 0;
}
else {
size = source.size.width;
x = 0;
y = (source.size.height - source.size.width)/2;
}
CGImageRef imageRef = CGImageCreateWithImageInRect([source CGImage], CGRectMake(x,y,size,size) );
return [UIImage imageWithCGImage:imageRef];
}
At this point I have no idea how to get this image to show up undistorted. It appears to be cropping it wrong and displaying it wrong despite the system saying that the image is indeed a square.
I abandoned that vocaro.com solution for other reasons (that solution would crash when used with atypical image formats, e.g. CMYK).
Anyway, I now use the imageByScalingAspectFillSize
method of the following UIImage
category to make my square thumbnails.
By the way, this automatically applies the scale of the main screen of the device (e.g. UIButton
button that is is 60x60 points on a Retina device, this will apply that scale of 2x (or 3x), i.e. a 120x120 or 180x180 image).
UIImage+SimpleResize.h:
/* UIImage+SimpleResize.h
*
* Modified by Robert Ryan on 5/19/11.
*/
@import UIKit;
/** Image resizing category.
*
* Modified by Robert Ryan on 5/19/11.
*
* Inspired by http://ofcodeandmen.poltras.com/2008/10/30/undocumented-uiimage-resizing/
* but adjusted to support AspectFill and AspectFit modes.
*/
@interface UIImage (SimpleResize)
/** Resize the image to be the required size, stretching it as needed.
*
* @param size The new size of the image.
* @param contentMode The `UIViewContentMode` to be applied when resizing image.
* Either `UIViewContentModeScaleToFill`, `UIViewContentModeScaleAspectFill`, or
* `UIViewContentModeScaleAspectFit`.
*
* @return Return `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode;
/** Resize the image to be the required size, stretching it as needed.
*
* @param size The new size of the image.
* @param contentMode The `UIViewContentMode` to be applied when resizing image.
* Either `UIViewContentModeScaleToFill`, `UIViewContentModeScaleAspectFill`, or
* `UIViewContentModeScaleAspectFit`.
* @param scale The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
*
* @return Return `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode scale:(CGFloat)scale;
/** Crop the image to be the required size.
*
* @param bounds The bounds to which the new image should be cropped.
*
* @return Cropped `UIImage`.
*/
- (UIImage * _Nullable)imageByCroppingToBounds:(CGRect)bounds;
/** Crop the image to be the required size.
*
* @param bounds The bounds to which the new image should be cropped.
* @param scale The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
*
* @return Cropped `UIImage`.
*/
- (UIImage * _Nullable)imageByCroppingToBounds:(CGRect)bounds scale:(CGFloat)scale;
/** Resize the image to fill the rectange of the specified size, preserving the aspect ratio, trimming if needed.
*
* @param size The new size of the image.
*
* @return Return `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingAspectFillSize:(CGSize)size;
/** Resize the image to fill the rectange of the specified size, preserving the aspect ratio, trimming if needed.
*
* @param size The new size of the image.
* @param scale The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
*
* @return Return `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingAspectFillSize:(CGSize)size scale:(CGFloat)scale;
/** Resize the image to be the required size, stretching it as needed.
*
* @param size The new size of the image.
*
* @return Resized `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingToFillSize:(CGSize)size;
/** Resize the image to be the required size, stretching it as needed.
*
* @param size The new size of the image.
* @param scale The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
*
* @return Resized `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingToFillSize:(CGSize)size scale:(CGFloat)scale;
/** Resize the image to fit within the required size, preserving the aspect ratio, with no trimming taking place.
*
* @param size The new size of the image.
*
* @return Return `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingAspectFitSize:(CGSize)size;
/** Resize the image to fit within the required size, preserving the aspect ratio, with no trimming taking place.
*
* @param size The new size of the image.
* @param scale The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
*
* @return Return `UIImage` of resized image.
*/
- (UIImage * _Nullable)imageByScalingAspectFitSize:(CGSize)size scale:(CGFloat)scale;
@end
UIImage+SimpleResize.m:
// UIImage+SimpleResize.m
//
// Created by Robert Ryan on 5/19/11.
#import "UIImage+SimpleResize.h"
@implementation UIImage (SimpleResize)
- (UIImage *)imageByScalingToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode {
return [self imageByScalingToSize:size contentMode:contentMode scale:0];
}
- (UIImage *)imageByScalingToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode scale:(CGFloat)scale {
if (contentMode == UIViewContentModeScaleToFill) {
return [self imageByScalingToFillSize:size];
}
else if ((contentMode == UIViewContentModeScaleAspectFill) ||
(contentMode == UIViewContentModeScaleAspectFit)) {
CGFloat horizontalRatio = self.size.width / size.width;
CGFloat verticalRatio = self.size.height / size.height;
CGFloat ratio;
if (contentMode == UIViewContentModeScaleAspectFill)
ratio = MIN(horizontalRatio, verticalRatio);
else
ratio = MAX(horizontalRatio, verticalRatio);
CGSize sizeForAspectScale = CGSizeMake(self.size.width / ratio, self.size.height / ratio);
UIImage *image = [self imageByScalingToFillSize:sizeForAspectScale scale:scale];
// if we're doing aspect fill, then the image still needs to be cropped
if (contentMode == UIViewContentModeScaleAspectFill) {
CGRect subRect = CGRectMake(floor((sizeForAspectScale.width - size.width) / 2.0),
floor((sizeForAspectScale.height - size.height) / 2.0),
size.width,
size.height);
image = [image imageByCroppingToBounds:subRect];
}
return image;
}
return nil;
}
- (UIImage *)imageByCroppingToBounds:(CGRect)bounds {
return [self imageByCroppingToBounds:bounds scale:0];
}
- (UIImage *)imageByCroppingToBounds:(CGRect)bounds scale:(CGFloat)scale {
if (scale == 0) {
scale = [[UIScreen mainScreen] scale];
}
CGRect rect = CGRectMake(bounds.origin.x * scale, bounds.origin.y * scale, bounds.size.width * scale, bounds.size.height * scale);
CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
UIImage *croppedImage = [UIImage imageWithCGImage:imageRef scale:scale orientation:self.imageOrientation];
CGImageRelease(imageRef);
return croppedImage;
}
- (UIImage *)imageByScalingToFillSize:(CGSize)size {
return [self imageByScalingToFillSize:size scale:0];
}
- (UIImage *)imageByScalingToFillSize:(CGSize)size scale:(CGFloat)scale {
UIGraphicsBeginImageContextWithOptions(size, false, scale);
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)imageByScalingAspectFillSize:(CGSize)size {
return [self imageByScalingAspectFillSize:size scale:0];
}
- (UIImage *)imageByScalingAspectFillSize:(CGSize)size scale:(CGFloat)scale {
return [self imageByScalingToSize:size contentMode:UIViewContentModeScaleAspectFill scale:scale];
}
- (UIImage *)imageByScalingAspectFitSize:(CGSize)size {
return [self imageByScalingAspectFitSize:size scale:0];
}
- (UIImage *)imageByScalingAspectFitSize:(CGSize)size scale:(CGFloat)scale {
return [self imageByScalingToSize:size contentMode:UIViewContentModeScaleAspectFit scale:scale];
}
@end