Square thumbnail from UIImagePickerViewController image
cgimage, ios, iphone, objective-c, thumbnails
Solution
The easiest thing to do would be to set the `contentMode` on your imageView to be `UIViewContentModeScaleAspectFill` instead. However, this may not be ideal since it would keep the whole image in memory.
Here is some code that I use to resize images.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size
{
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
This version will keep the image from being distorted if the size is not the same aspect ratio as the image.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToFillSize:(CGSize)size
{
CGFloat scale = MAX(size.width/image.size.width, size.height/image.size.height);
CGFloat width = image.size.width * scale;
CGFloat height = image.size.height * scale;
CGRect imageRect = CGRectMake((size.width - width)/2.0f,
(size.height - height)/2.0f,
width,
height);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
[image drawInRect:imageRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Often you want just the top square of an image (rather than the center). And you want the final image to be of a certain size, say 128x128, like in the example below.
- (UIImage *)squareAndSmall // as a category (so, 'self' is the input image)
{
// fromCleverError's original
// http://stackoverflow.com/questions/17884555
CGSize finalsize = CGSizeMake(128,128);
CGFloat scale = MAX(
finalsize.width/self.size.width,
finalsize.height/self.size.height);
CGFloat width = self.size.width * scale;
CGFloat height = self.size.height * scale;
CGRect rr = CGRectMake( 0, 0, width, height);
UIGraphicsBeginImageContextWithOptions(finalsize, NO, 0);
[self drawInRect:rr];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Problem
In my app the user selects an image or take a picture using UIImagePickerViewController. Once the image was selected I want to display its thumbnail on a square UIImageView (90x90). I'm using Apple's code to create a thumbnail. The problem is the thumbnail is not squared, the function, after set kCGImageSourceThumbnailMaxPixelSize key to 90, seems only to resize the image's height, and as far as I know the kCGImageSourceThumbnailMaxPixelSize key should be responsible for setting the height and width of the thumbnail. Here is a glimpse of my code: ``` - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; NSData *imageData = UIImageJPEGRepresentation (image, 0.5); // My image view is 90x90 UIImage *thumbImage = MyCreateThumbnailImageFromData(imageData, 90); [myImageView setImage:thumbImage]; if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); } [picker dismissViewControllerAnimated:YES completion:nil]; } UIImage* MyCreateThumbnailImageFromData (NSData * data, int imageSize) { CGImageRef myThumbnailImage = NULL; CGImageSourceRef myImageSource; CFDictionaryRef myOptions = NULL; CFStringRef myKeys[3]; CFTypeRef myValues[3]; CFNumberRef thumbnailSize; // Create an image source from NSData; no options. myImageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); // Make sure the image source exists before continuing. if (myImageSource == NULL){ fprintf(stderr, "Image source is NULL."); return NULL; } // Package the integer as a CFNumber object. Using CFTypes allows you // to more easily create the options dictionary later. thumbnailSize = CFNumberCreate(NULL, kCFNumberIntType, &imageSize); // Set up the thumbnail options. myKeys[0] = kCGImageSourceCreateThumbnailWithTransform; myValues[0] = (CFTypeRef)kCFBooleanTrue; myKeys[1] = kCGImageSourceCreateThumbnailFromImageIfAbsent; myValues[1] = (CFTypeRef)kCFBooleanTrue; myKeys[2] = kCGImageSourceThumbnailMaxPixelSize; myValues[2] = thumbnailSize; myOptions = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 2, &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); // Create the thumbnail image using the specified options. myThumbnailImage = CGImageSourceCreateThumbnailAtIndex(myImageSource, 0, myOptions); UIImage* scaled = [UIImage imageWithCGImage:myThumbnailImage]; // Release the options dictionary and the image source // when you no longer need them. CFRelease(thumbnailSize); CFRelease(myOptions); CFRelease(myImageSource); // Make sure the thumbnail image exists before continuing. if (myThumbnailImage == NULL) { fprintf(stderr, "Thumbnail image not created from image source."); return NULL; } return scaled; } ``` And this how my image view is instantiated: ``` myImageView = [[UIImageView alloc] init]; imageView.contentMode = UIViewContentModeScaleAspectFit; CGRect rect = imageView.frame; rect.size.height = 90; rect.size.width = 90; imageView.frame = rect; [imageView setUserInteractionEnabled:YES]; ``` If I don't set `imageView.contentMode = UIViewContentModeScaleAspectFit;` the thumbnail will be distorted, since it is just a version of my original image with height of 90 pixels. So, why is my thumbnail not squared?