Today I wrote a useful Objective-C method which uses Quartz 2D from the iPhone SDK to dynamically generate square thumbnails from any portfolio or landscape UIImage. I failed to find helpful results on Google for the various CGContextClipToRect/CGContextTranslateCTM/CGContextScaleCTM Quartz functions so after a lot of trial and error I managed to get the results I was after with a fairly efficient script – shown below.
So, if for example you want to get a 32×32 pixel square thumbnail, you can pass 32 as the ‘length’ parameter in the function below. For portfolio images the code will take a full square from the original image from the centre using the full width of your original portfolio image. Likewise, for a landscape image the script will use all the height available, clipping the right and left sides.
I think the most import thing I learned when writing this script is that when you call UIGraphicsBeginImageContext(CGSize) make sure that the size you pass in is the size you want your final image to be. Otherwise you end up having to call UIGraphicsEndImageContext() and UIGraphicsBeginImageContext() multiple times with different CGSize instances which I’m sure isn’t an optimised way to achieve the same results.
- (UIImage *)thumbWithSideOfLength:(float)length {
NSString *subdir = @”my/images/directory”;
NSString *filename = @”myOriginalImage.png”;
NSString *fullPathToThumbImage = [subdir stringByAppendingPathComponent:[NSString stringWithFormat:@"%dx%d%@",(int) length, (int) length,filename];
NSString *fullPathToMainImage = [subdir stringByAppendingPathComponent:filename];UIImage *thumbnail;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:fullPathToThumbImage] == YES) {
thumbnail = [UIImage imageWithContentsOfFile:fullPathToThumbImage];}
else {
//couldn’t find a previously created thumb image so create one first…
UIImage *mainImage = [UIImage imageWithContentsOfFile:fullPathToMainImage];UIImageView *mainImageView = [[UIImageView alloc] initWithImage:mainImage];
BOOL widthGreaterThanHeight = (mainImage.size.width > mainImage.size.height);
float sideFull = (widthGreaterThanHeight) ? mainImage.size.height : mainImage.size.width;CGRect clippedRect = CGRectMake(0, 0, sideFull, sideFull);
//creating a square context the size of the final image which we will then
// manipulate and transform before drawing in the original image
UIGraphicsBeginImageContext(CGSizeMake(length, length));
CGContextRef currentContext = UIGraphicsGetCurrentContext();CGContextClipToRect( currentContext, clippedRect);
CGFloat scaleFactor = length/sideFull;
if (widthGreaterThanHeight) {
//a landscape image – make context shift the original image to the left when drawn into the context
CGContextTranslateCTM(currentContext, -((mainImage.size.width – sideFull) / 2) * scaleFactor, 0);}
else {
//a portfolio image – make context shift the original image upwards when drawn into the context
CGContextTranslateCTM(currentContext, 0, -((mainImage.size.height – sideFull) / 2) * scaleFactor);}
//this will automatically scale any CGImage down/up to the required thumbnail side (length) when the CGImage gets drawn into the context on the next line of code
CGContextScaleCTM(currentContext, scaleFactor, scaleFactor);[mainImageView.layer renderInContext:currentContext];
thumbnail = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(thumbnail);[imageData writeToFile:fullPathToThumbImage atomically:YES];
thumbnail = [UIImage imageWithContentsOfFile:fullPathToThumbImage];
}
return thumbnail;
}
So if, for example your original image looked like this:

Using the method above you could request a 100×100 pixel thumbnail which would result in this:
![]()
Dude, this was just what I was looking for. Awesome post. Kudos to you
Hi Nick,
Your method to create a thumbnail is just what I need. May I incorporate this in the iPhone app that I am developing? Will gladly give you credit in the class.
Thanks, Marc
Hi Marc – yes, of course you can use the code. With or without a credit
Thanks again Nick. It is working out great for me.
You’re very welcome
Thanks! This was exactly what I’m looking for! Great work!
Thanks for the great piece of code. You are leaking memory with the mainImageView. You should release it
THanks again!
Wow, what a great piece of code! Like everyone else has said, this is exactly what I am looking for. It creates thumbnails in almost exactly the same position as those in the standard apple photos app.
A little hint to those who are having any troubles using the code: if you run into any errors, try typing out the code instead of copy/paste. Sometimes it messes up a few characters.
Thanks Nick!
Thanks a lot. The code is very instructive.
Thanks, this is great.
BTW you’ve got en-dashes rather than regular dashes in the middle of the two CGContextTranslateCTM lines, so the code won’t compile.
Thanks a lot for the useful information. What I need is to thumbnail a view. Is anyone have any idea for that?
Thanks in advance.
Hi, Thanks for the sample!
But I have some kind of allocation problem with this code, It seems that the UIImage returned by UIGraphicsGetImageFromCurrentImageContext is never released…
I’ve used this method to create blocks of 5 thumbnails lazy loaded in a uiscrollView, Everything is released correctly but the memory allocation still grows..
Any idea?
(I’ve already ensured to call it from the mean thread by the way)
Thanks
Is there a way to create thumbnails for a video file similar to this?
Lionel: I have this same problem did, you find solution?
A Ryan mentioned, mainImageView is leaking memory.
This change should resolve it:
[mainImageView.layer renderInContext:currentContext];
change to :
[mainImageView.layer renderInContext:currentContext];
[mainImageView release];mainImageView=nil;
Hey Nick
Big Gr33tz for you, I wrote in many places because i had issue whit similar program nobody could help me, I tought that this is some kind of bug in renderInContext, I’m so fucking happy that this nightmare is over
Regards,
x51
In the example above I don’t understand why you use -renderInContext: at all. Why not just draw the image itself instead? That would do the exact same thing but without requiring QuartzCore and all the overhead -renderInContext: adds to this.