Undocumented UIImage resizing

Last week, I was trying to resize an image in the iPhone SDK. Those who don’t know anything about Objective-C and are not interested should stop reading right… here.


Alright. Those who are still with me here know what I’m talking about: there is no easy documented way to resize a UIImage object. Unlike NSImage (on the Mac OSX platform) which supports the message setSize:, UIImage documentation is lacking any such methods.

Oh wait. That exists, though it’s the undocumented message _imageScaledToSize:interpolationQuality:… and unfortunately, what is undocumented from Apple will soon be unsupported or renamed. And it gives a warning about “method not found”, of course, which can be circumvented with the following category:

// WARNING: these methods are not documented, but exist as of SDK version 2.1
@interface UIImage (UndocumentedUIImage)
- (UIImage*)_imageScaledToSize:(CGSize)newSize interpolationQuality:(float)aQuality;
@end

Nothing new under the sun, it has been documented elsewhere on the net . But how can we build a similar function using only Apple-approved (at this time) code? The answer is quite simple, though

First, we create a small graphic context with the new size we want to give it. Then you draw the image in there, and take it back. We pack it all nicely in a category and hop! instant wootness.

@interface UIImage (INResizeImageAllocator)
+ (UIImage*)imageWithImage:(UIImage*)image
              scaledToSize:(CGSize)newSize;
- (UIImage*)scaleImageToSize:(CGSize)newSize;
@end
 
@implementation UIImage (INResizeImageAllocator)
+ (UIImage*)imageWithImage:(UIImage*)image 
               scaledToSize:(CGSize)newSize;
{
   UIGraphicsBeginImageContext( newSize );
   [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
   UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();
 
   return newImage;
}
- (UIImage*)scaleImageToSize:(CGSize)newSize
{
   return [UIImage imageWithImage:self scaledToSize:newSize];
}
@end

Just copy this code somewhere, the @interface part in a .h and the @implementation where it belongs. You have an allocator and a member function that looks close enough to the undocumented way. There is no quality parameter, unfortunately we don’t have control over this anymore.

To be honest, it is probably slower than the unofficial way. It probably use custom code instead of creating and destroying a context, and the quality parameter hints to some optimizations. Also, it is very probably that Apple will include the _imageScaledToSize:interpolationQuality: selector in a future version of the SDK, and rename it.

Until then, this method is the simplest way to go.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • Print
  • Reddit
  • StumbleUpon
  • Technorati
  • TwitThis
  • Wikio

Comments (20)

AndyOctober 31st, 2008 at 9:50 am

Brilliant. Simple and neat solution to a vexing problem. I guess they left the resizing out because most of the time you’ll be using UIImageView, which handles the scaling for you. Unfortunately, the UIImageView for an image on a UITableViewCell isn’t accessible, so you’re stuffed if you don’t manually resize your image to the correct size.

Thanks !

PoltrasOctober 31st, 2008 at 9:57 am

Actually, you can put UIImageView on a UITableViewCell by putting it as a subview. Nothing wrong with that. Something along:

- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
   if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
      UIImageView* imgView = [[UIImageView alloc] initWithFrame:CGMakeRect(0,0,30,frame.size.height)];
      imgView.image = [UIImage imageWithName:@"anImage.png"];
      [self addSubview:[imgView autorelease]];
   }
   return self;
}

Should work out of the box. But what I use my solution for mainly is when taking pictures in the image album and then using them in a CALayer, which cannot take images larger than 1024×1024. Also, managing resources by releasing the original… saves a lot of memory.

ThornyNovember 2nd, 2008 at 9:15 am

The solution is great and seems to be working for everyone. But I’ve got a problem here. I need to save a smaller version of an image to file, so I do the following:

	UIImage *initialImage = [UIImage imageNamed:@"SomeName.jpg"];
 
	UIGraphicsBeginImageContext(CGSizeMake(176.0, 235.0));
	[initialImage drawInRect:CGRectMake(0.0, 0.0, 176.0, 235.0)];
	UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
 
	NSData *jpegData = UIImageJPEGRepresentation(newImage, 0.5);
        [jpegData writeToFile:imagePath atomically:NO];

The file it creates is of the required size, but blank – just a white rectangle. The undocumented method does just the same. I’m totally noob, so my mistake must be painfully stupid, but I’m stuck here. Any ideas what could be wrong?

Thank you!

ThornyNovember 2nd, 2008 at 9:25 am

I’ve just found out that it works when the initial picture is of PNG format, but it makes blank resized images for JPG. Must be some obvious reason, but I just don’t get it.

PS What is the correct tag for quoting code in here?

PoltrasNovember 3rd, 2008 at 12:10 pm

Hey Thorny,

The right way to put code here is with the <pre lang=”objc”>. For syntax highlighting, you can change the lang parameter. There is also a way to put line numbers in there. I might change the syntax plugin to use a different tag and put something above the textarea in the theme to list the available tags.

I’m gonna try with a JPG and a PNG to see what could be the problem. I’m as much surprised as you are.

waltyNovember 4th, 2008 at 4:24 am

very nice, thanks :)

Picture 15 v1.0 - apps.innobec.comNovember 7th, 2008 at 11:48 am

[...] also made a post here about how to resize images [...]

DavidPhillipOsterJanuary 21st, 2009 at 12:39 pm

If you don’t like undocumented calls, you can just create a new bitmap graphic context, make it the current context, set up the correct affine transform, draw the source into it with CGContextDrawImage(), and tell it to hand you back the UIImage.

If you don’t need the image handed back, then just set the correct affine transform of the current context, and CGContextDrawImage() the source into it

GMJanuary 30th, 2009 at 11:14 pm

I’m newbie and curious. Will i be responsible for releasing the image received by the created methods?

PoltrasFebruary 1st, 2009 at 4:47 pm

@GM: the image is autoreleased. Check out UIGraphicsGetImageFromCurrentImageContext() documentation for details.

@DavidPhillipOster: That’s what I do, but I use drawInRect instead of using bitmap+transform. Your method might be faster, but not by much.

JoachimFebruary 4th, 2009 at 12:34 am

Given that this category contains drawing code, you’ll need to make sure you only resize images on the main thread. (I would get EXEC_BAD_ACCESS errors whenever I resized images on a separate thread while running on the device.)

jrcMarch 3rd, 2009 at 8:03 am

Thanks. Nitpick: I would have named it -imageByScalingToSize: instead of -scaleImageToSize:, following the Cocoa naming convention of hinting what type will be returned.

ADJune 7th, 2009 at 3:00 pm

Thanks for this post!
Has anyone found a solution for scaling JPEG ?

ITCohortsJune 9th, 2009 at 1:34 pm

Thank you!!! This works great.

[...] be skewed so cropping was required. All of the examples I found either focused on cropping only or resizing but I couldn’t find any that did [...]

DavidAugust 15th, 2009 at 11:24 am

Awesome, thanks for posting this. Just what I needed.

bobAugust 21st, 2009 at 4:51 pm

Does anyone know what the “interpolationQuality:” parameter does? And how to replicate that behavior if you implement it yourself? Thanks,

mzSeptember 19th, 2009 at 7:32 pm

See CGContextSetInterpolationQuality(bitmap, interpolationQuality);

mzSeptember 29th, 2009 at 3:30 am

- (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize
iQ:(double)interpolationQuality;
{
UIGraphicsBeginImageContext( newSize );
CGContextSetInterpolationQuality(UIGraphicsGetCurrentContext(), interpolationQuality);
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return newImage;
}

Leave a comment

Your comment