Resize a UIImage the right way

When deadlines loom, even skilled and experienced programmers can get a little sloppy. The pressure to ship may cause them to cut corners and look for a quick and easy solution, even if that solution is sure to cause trouble later on. Eventually, their coding style devolves into copy and paste programming, a lamentable tactic that involves cherry-picking snippets of code from a past project and putting them to use in the current one. Of course, the proper solution is to factor out the code into some kind of reusable library, but due to time constraints, it’s simply duplicated wherever it’s needed. Any bugs in the original code have now spread to a dozen different places in a dozen different projects. It’s an algorithm for chaos.

Yet in the world of iPhone applications, copy and paste programming seems to be disturbingly common. The fact that so many iPhone apps are short-term, one-off projects doesn’t help, but the situation has been aggravated even more by Apple’s security restrictions. In particular, dynamic linking to any shared library that doesn’t ship with the OS is strictly forbidden. One could argue that this rule is a necessary side-effect of the iPhone’s sandboxing security model, but even workarounds such as consolidating code into a static shared library are extraordinarily difficult. Another contributing factor is that the iPhone API is still relatively immature, and developers too often require custom code to fill in its gaps.

This situation has transformed more than a few iPhone programmers into copy and paste programmers. When they inevitably encounter some limitation with the iPhone API, the typical response is:

  1. Search online for a solution
  2. Find a snippet of code somewhere that gets the job done (usually at Stack Overflow or iPhone Dev SDK)
  3. Copy and paste the snippet into their project
  4. Move on to the next problem

Now imagine what happens when a thousand iPhone developers find the same snippet. Suddenly the problems of copy and paste programming have gone global. Offline, a bug in a single snippet of code may infect a dozen projects; online, it can spread to thousands.

As a reluctant copy and paste iPhone programmer myself, I’ve witnessed this scenario first-hand. I recently encountered a limitation with a certain iPhone class—UIImage—and I found in a discussion forum what seemed to be a popular, well-regarded solution. The code snippet was the first hit in a Google search, and many readers had replied with thanks to its author. However, a bit of testing showed that it worked for most images but completely failed for others. By the time I stumbled upon it, the buggy code had probably spread to countless programs already.

In the process of finding the bug and posting the fix, I ended up writing a substantial amount of additional code to address various other annoyances related to UIImage. The complete listing is available for download below. Though it won’t solve the copy and paste problem, it should be a welcome remedy for other iPhone developers who have run into similar obstacles.

Background

Programming for the iPhone, a highly graphical device, necessarily involves a substantial amount of image manipulation. Its SDK therefore provides an abstraction called UIImage that handles much of the effort in importing and drawing images. For example, imagine you want to load a JPEG file, scale it down to icon size, and give it rounded corners. These tasks may require tens or even hundreds of lines of code on other platforms, but on the iPhone, it’s only a matter of instantiating a UIImage, passing it to a UIImageView of the appropriate size, and setting the cornerRadius property.

Despite its ease of use, or perhaps because of it, UIImage suffers from some serious limitations. Key among these is its lack of support for resizing the image, a feature that is normally handled dynamically by its companion, the UIImageView component. However, should an iPhone application need to reduce the size of an image for storage or for exchange with an external entity (such as a web server), the UIImage class is insufficient.

Of course, UIImage is not the only means of image manipulation on the iPhone. It ships with a rather sophisticated graphics API, known as Quartz 2D, that offers low-level control of bitmap data. Clearly, the functionality for resizing an image exists, although taking advantage of it is not straightforward and requires the developer to write non-trivial code. How best to accomplish this task has been the source of much confusion and debate, particularly in forums such as iPhone Dev SDK:

  • Resizing a photo to a new UIImage

    This is crazy. I know there are threads that touch on this already, but none of them have led me to the answer. I can’t believe that it is really this difficult!

  • Resize Image High Quality

    I have done lots of searching for a way to resize images via the iPhone SDK and I have come across a few methods which work but the resulting image does not look nearly as good as if you took the full resolution image and told it to draw inside a rectangle.

These discussions have resulted in countless code snippets that claim to resize a UIImage, but many of them contain bugs, or they simply leave out functionality such as EXIF orientation support, an absolute necessity when dealing with photographs taken by the iPhone’s camera. For instance, a particularly popular code snippet for UIImage resizing incorrectly processes alpha information, resulting in a pink tint for certain image files.

Image resized correctly
Image resized correctly

Image resized with pink tint
Image resized with buggy code

A Better Way to Resize Images

The following sections describe yet another collection of source code for resizing UIImage objects. Functionally, it is similar to code samples that can be found elsewhere on the Internet in discussion forums and blogs, but it consolidates their features into a self-contained, reusable package and offers several notable improvements:

  • Additional methods for cropping images, generating thumbnails, and more.
  • Implemented as Objective-C categories to facilitate reuse. With categories, you can simply plop the code into your project, import a header file, and all of your UIImage objects will automatically have access to the new methods.
  • Bugs that commonly plague other code of this type have been found and fixed. The categories have been vetted in a large, real-world iPhone app, and they contain no known bugs.
  • The code has been simplified as much as possible and is more thoroughly documented.

The source code to the categories can be downloaded from the links below or as a single archive. If you are an experienced iPhone programmer, you can probably grab the files and start using them right away. Continue reading for more detail on how to apply them, as well as a run-down of the problems that prompted their creation.

UIImage+Resize.h, UIImage+Resize.m
Extends the UIImage class to support resizing (optionally preserving the original aspect ratio), cropping, and generating thumbnails.

UIImage+RoundedCorner.h, UIImage+RoundedCorner.m
Extends the UIImage class to support adding rounded corners to an image.

UIImage+Alpha.h, UIImage+Alpha.m
Extends the UIImage class with helper methods for working with alpha layers (transparencies).

UIImage+Alpha

The Alpha category is perhaps not as directly useful as the others, though it provides some necessary functionality that they build upon. Its methods include:

- (BOOL)hasAlpha;

Tells whether the image has an alpha layer.

- (UIImage *)imageWithAlpha;

Returns a copy of the image, adding an alpha channel if it doesn’t already have one. An alpha is required when adding transparent regions (e.g., rounded corners) to an image. It may also be necessary when loading certain kinds of image files that are not directly supported by Quartz 2D. For example, if you load a JPEG using imageNamed:, the resulting UIImage will have 32 bits per pixel with the first 8 bits unused (kCGImageAlphaNoneSkipFirst). But if you take the same image and save it in BMP format, and load it exactly the same way, the UIImage will have 24 bits per pixel (kCGImageAlphaNone), which is unsupported in Quartz 2D. Trying to render it to a graphics context will cause run-time errors. The obvious way around this problem is to make sure you only load image files that produce a Quartz-compatible pixel format. (A complete list is available in the Supported Pixel Formats section of the Quartz 2D Programming Guide.) If for some reason this is not possible, adding an alpha channel to the UIImage at runtime may also work.

- (UIImage *)transparentBorderImage:(NSUInteger)borderSize;

Returns a copy of the image with a transparent border of the given size added around its edges. This solves a special problem that occurs when rotating a UIImageView using Core Animation: Its borders look incredibly ugly. There’s simply no antialiasing around the view’s edges. Luckily, adding a one-pixel transparent border around the image fixes the problem. The extra border moves the visible edges of the image to the inside, and because Core Animation interpolates all inner pixels during rotation, the image’s borders will magically become antialiased. This trick also works for rotating a UIButton that has a custom image. The following before-and-after video shows the technique in action. (The top square is the original image; the bottom square has a one-pixel transparent border.)

Get the Flash Player to see this content.

UIImage+RoundedCorner

With the release of iPhone OS 3.0, a new Core Animation feature called cornerRadius became available. When applied to a layer, it makes the corners soft and round, just the thing for achieving a Web 2.0 or Mac OS X look-and-feel. For example, if you have a UIButton with a custom image like this:

Square corners

And add a couple lines of code:

button.layer.cornerRadius = 30;
button.layer.masksToBounds = YES;

You get this:

Rounded corners

The fun stops there. The cornerRadius setting only affects the run-time appearance of the layer. As soon as you save the image or send it over the network, the rounded corners go away. Also, if you animate the layer, perhaps by making it rotate, the cornerRadius property mysteriously reverts to zero, giving the image sharp corners again. This is a confirmed bug (#7235852) in iPhone OS 3.0 and 3.1.

To fix this problem, the RoundedCorner category can apply rounded corners to a UIImage permanently. It modifies the image data itself, adding an alpha layer if necessary. Not only does this work around the Core Animation bug, it also preserves the rounded corner effect when exporting the UIImage to a file or network stream, assuming that the output format supports transparency.

The category exposes a single method:

- (UIImage *)roundedCornerImage:(NSInteger)cornerSize
                     borderSize:(NSInteger)borderSize;

Creates a copy of the image, adding rounded corners of the specified radius. If borderSize is non-zero, a transparent border of the given size will also be added. (The primary purpose of this parameter is to work around the aforementioned aliasing problem that occurs when rotating an image view.) The implementation is based on code by Björn Sållarp.

UIImage+Resize

Resizing a UIImage is more complicated than it may seem. First, there’s simply the matter of learning Quartz 2D—a somewhat complex, low-level API. A mistake in a single parameter can suddenly affect thousands of pixels, yielding unexpected results like the pink tint problem shown previously.

Another issue to consider is the quality of the resulting image. By default, Quartz 2D applies a fast but not-so-high-quality interpolation algorithm when scaling images up or down. The effect is especially noticeable when reducing an image to a very small size, perhaps for an icon or thumbnail representation. The aliasing caused by the algorithm transforms smooth lines into jagged edges. Faces become a pixelated mess.

To illustrate, the following image is the result of squeezing a 1024×516-pixel JPEG (courtesy of PD Photo) into a 320×200-pixel UIImageView with automatic resizing enabled:

Resize without interpolation

Note the serrated edges along the wings. To counteract the unsightliness, Quartz 2D can be configured for a different scaling algorithm by calling CGContextSetInterpolationQuality. Here is the same image, pre-scaled using the kCGInterpolationHigh option, and displayed in the same UIImageView:

Resize with interpolation

The jaggies are now gone, replaced with smoother, cleaner lines.

Yet another obstacle, one of particular importance to iPhone developers, is image orientation. When a user takes a snapshot with the iPhone’s camera, the image is not upright but is in fact rotated 90 degrees counterclockwise. The reason is because the iPhone’s camera is positioned in a way that makes up (from the lens’s perspective) point to the left-hand side of the camera. The iPhone’s camera software knows this and therefore adds a special flag to the image data that indicates how the pixels should be rotated to produce the correct orientation. The software employs the same tactic when the user takes a picture in landscape mode (i.e., holding the phone sideways). It can rotate the image without having to apply a transformation across millions of pixels. Instead, it simply changes the orientation flag. Components such as UIImageView automatically read this flag—stored in the imageOrientation property of UIImage—and apply the proper rotation at run-time when displaying the image.

Unfortunately, as soon as you dip into the low-level Quartz 2D API, which has no knowledge of the high-level UIImage class, the orientation information is lost. An image resize algorithm written using this API will need to be provided with the orientation and perform the rotation explicitly.

The Resize category solves each of these problems while incorporating additional handy features. Its methods include:

- (UIImage *)croppedImage:(CGRect)bounds;

Returns a copy of the image that is cropped to the given bounds. The bounds will be adjusted using CGRectIntegral, meaning that any fractional values will be converted to integers.

- (UIImage *)thumbnailImage:(NSInteger)thumbnailSize
          transparentBorder:(NSUInteger)borderSize
               cornerRadius:(NSUInteger)cornerRadius
       interpolationQuality:(CGInterpolationQuality)quality;

Returns a copy of the image reduced to the given thumbnail dimensions. If the image has a non-square aspect ratio, the longer portion will be cropped. If borderSize is non-zero, a transparent border of the given size will also be added. (The primary purpose of this parameter is to work around the aforementioned aliasing problem that occurs when rotating an image view.) Finally, the quality parameter determines the amount of antialiasing to perform when scaling the image.

- (UIImage *)resizedImage:(CGSize)newSize
     interpolationQuality:(CGInterpolationQuality)quality;

Returns a resized copy of the image. The quality parameter determines the amount of antialiasing to perform when scaling the image. Note that the image will be scaled disproportionately if necessary to fit the specified bounds. In other words, the aspect ratio is not preserved.

This method, as well as all other methods described here that perform resizing, takes into account the orientation of the UIImage and transforms the pixels accordingly. The resulting image’s orientation will be up (UIImageOrientationUp), regardless of the current orientation value. The code to perform this transformation is based in part on the following sources:

- (UIImage *)
  resizedImageWithContentMode:(UIViewContentMode)contentMode
                       bounds:(CGSize)bounds
         interpolationQuality:(CGInterpolationQuality)quality;

UIImageView offers a remarkably helpful ability: It can resize displayed images while preserving their aspect ratio. The manner of preservation depends on a setting known as the content mode. For example, if a large JPEG (courtesy of PD Photo) is displayed in a small view with the content mode set to Center (UIViewContentModeCenter), only a portion of the image is visible:

Content mode - unscaled

To include the entire image, the view’s content can be scaled to fit within the bounds (UIViewContentModeScaleToFill). Note that Scale To Fill does not preserve the aspect ratio, resulting in a squashed image:

Content mode - scaled

To scale the image without changing the aspect ratio, one option is to shrink the content until it fits entirely within the bounds (UIViewContentModeScaleAspectFit). Although this option shows the full image, it has the side-effect of not filling the entire view:

Content mode - aspect fit

(Note that any area not covered by the image in Aspect Fill mode is actually transparent. It’s colored gray here to show the view boundary.)

Another aspect-preserving option is to shrink the content just enough to fit the smaller dimension within the view. The larger dimension (in this case, the length) will be cropped:

Content mode - aspect fill

The correct choice of content mode depends, of course, on the desired appearance and the nature of the source image.

Because these modes are so useful, equivalent functionality has been rolled into the Resize category. Scale To Fill is the default behavior of resizedImage:interpolationQuality:, while resizedImageWithContentMode: supports both Aspect Fit and Aspect Fill. (Other content modes, such as Left and Bottom Right, were left unimplemented because they are rarely used.)

License

All code presented here is free for both personal and commercial use, with or without modification. No warranty is expressed or implied.


 

219 Responses to “Resize a UIImage the right way”

  1. cybervedaa says:

    I have been using your code on an app i am developing and i must say, it is a lifesaver. However, i recently discovered that it is behaving weired when used with iOS5. When i resize an image taken in portrait mode, it is rotating 90 degrees to the left. An even weirder thing is that this does not happen to photos taken on the same iphone that are older (Taken when my iPhone 4 was running iOS4 and iOS5 betas).
    I still dont understand image manipulation well enough to write my own code for this, so i googled for alternatives and found this http://stackoverflow.com/questions/538041/uiimagepickercontroller-camera-preview-is-portrait-in-landscape-app .
    The code at the posted link works fine with iOS5. It might help in making your code iOS5 compatible.
    Once again thank you for your code.

  2. William K says:

    I am getting runtime errors on iOS 5:

    : CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaLast; 416 bytes/row.

    : CGContextConcatCTM: invalid context 0×0

    : CGContextSetInterpolationQuality: invalid context 0×0

    : CGContextDrawImage: invalid context 0×0

    : CGBitmapContextCreateImage: invalid context 0×0

    Any idea why?

    • Gabriele says:

      I got the same error but only when I run inside the Simulator. On iOS 5 and earlier version everything is OK.

    • Andy says:

      I am seeing this in the Simulator as well as on the device.

    • horseshoe7 says:

      I’m getting this error as well. Does anyone know what it could be? Please help!

      • horseshoe7 says:

        I “fixed” the problem in that the error comes from being unable to create the context due to invalid input parameters (which are being read from the original image, which may have been encoded wrong).

        When you think about it, who cares so much about the input file because we are defining here what the output image will be. So, in UIImage+Resize.m around line 120, replace the line CGContextRef bitmap = … with

        CGContextRef bitmap = CGBitmapContextCreate(
        NULL,
        newRect.size.width,
        newRect.size.height,
        8, /* bits per channel */
        (newRect.size.width * 4), /* 4 channels per pixel * numPixels/row */
        CGColorSpaceCreateDeviceRGB(),
        kCGImageAlphaPremultipliedLast
        );

        and that seemed to make the errors go away. Images look ok too.

  3. Bharat Biswal says:

    Did anyone find a solution for issues on iOS 5 simulator and device.

  4. Joe says:

    I am having the same run of errors on my iOS 5 device:

    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextConcatCTM: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextSetInterpolationQuality: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextDrawImage: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGBitmapContextCreateImage: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextDrawImage: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGBitmapContextCreateImage: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextBeginPath: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextAddRect: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextClosePath: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : clip: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGContextDrawImage: invalid context 0×0
    Nov 4 15:27:33 Joe-Merediths-iPhone Autoscroll[950] : CGBitmapContextCreateImage: invalid context 0×0

    But along with that resizing the image to a thumbnail is not working, instead of a smaller image the method creates a much larger image:
    // Access the uncropped image from info dictionary
    UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];

    [image thumbnailImage:0.5 transparentBorder:0.2 cornerRadius:0.2 interpolationQuality:0.2];

    Any help with this would be much appreciated

  5. Avery says:

    Your cropping method will occasionally rotate images when cropping.

    A simple change fixes it though.
    Instead of

    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];

    Use:

    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:self.imageOrientation];

  6. [...] flip and/or rotate the image according to its orientation value.Being my lazy self I used the fine code of Trevor Harmon in UIImage+Resize. Trevor added some categories to make handling UIImage a bit [...]

  7. Salem Sayed says:

    To fix those errors, i re-sized the imaged before adding the rounded rect to 48×48, that magically solved everything.

  8. Matt says:

    I was having the same issues on iOS5. This fixed it.

    Change the resizeImage method in UIImage+Resize.m to directly specify rather than derive the colour space and alpha information.

    // make the following changes.

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef bitmap = CGBitmapContextCreate(NULL, newRect.size.width, newRect.size.height, 8, 4 * newRect.size.width, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGColorSpaceRelease(colorSpace);

    // rest of code continues.

    Cheers,
    Matt

  9. nik says:

    Anyone care to create new versions of these categories that include the above fixes? Not for me, I am going to do it now but for others who might need it…

    This is also missing the one thing I need which is scale an image to a (single) max width/height, preserving the aspect ratio. Why should thumbnails be square, that makes no sense to me. If I am displaying square thumbnails, it’s much better to handle this on the UIImageView level. Let the view decide whether to display the image as aspect fit, fill, etc rather than putting this in the scaled image.

    • Trevor says:

      The thumbnail code does preserve aspect ratio; it uses UIViewContentModeScaleAspectFill. It produces a square image because thumbnails are usually squared off. (See iPhoto’s “Events” view for an example.) But it would be nice to offer more flexibility.

  10. Tal says:

    Hi.

    I want to take images taken with the iphone camera (landscape or portrait) and resize them to a manageable size (around 120kb) while preserving most of the quality.

    What should I send as the newSize for the resizedImage? 640×480 or 480×640 ?

    I didn’t understand the “result will always be “up”.
    it means the resized image will always result in 480×640 ?

    Thanks alot!

  11. Bill Frowine says:

    Hi I was just using the UIImage+Resize category and am getting the following errors:

    1. Semantic Issue enumeration values uiimage orientation not handled in switch. here is the method it is referring to:

    switch (self.imageOrientation) {
    case UIImageOrientationDown: // EXIF = 3
    case UIImageOrientationDownMirrored: // EXIF = 4
    transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height);
    transform = CGAffineTransformRotate(transform, M_PI);
    break;

    2. symantec issue 4 enumeration values not handled in switch uiimage orientation up, uiimage orientaton down, uiimage orientation left, uiimage orientation right. Here is the method:

    switch (self.imageOrientation) {
    case UIImageOrientationUpMirrored: // EXIF = 2
    case UIImageOrientationDownMirrored: // EXIF = 4
    transform = CGAffineTransformTranslate(transform, newSize.width, 0);
    transform = CGAffineTransformScale(transform, -1, 1);
    break;

    • ultravioletu says:

      Simply add “default: break;” statements to both switch() blocks…

      • Peter says:

        These only appear as warnings, at least for me. Still, it’s not very reassuring when code which is supposed to do things ‘the right way’ causes build warnings.

        Not meaning to be ungrateful, mind :)

  12. Boris says:

    newbies, please look at
    http://eran.sandler.co.il/2011/11/07/uiimage-in-ios-5-orientation-and-resize/

    to fix a trick with rotations on ios5

  13. Russ Wicks says:

    I am using:

    UIGraphicsBeginImageContextWithOptions(size,YES, 2.0);
    [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    and whilst this does not resize my image AND keep aspect ratio the quality is far greater. Your code does not appear to give the option of taking advantage of the retina display.

    • Greg Maletic says:

      I noticed the implementation of -[UIImage transparentBorderImage:borderWidth] doesn’t work on retina devices. Here’s a new implementation I made that does seem to work:

      - (UIImage*)transparentBorderImage:(NSUInteger)borderWidth
      {
      CGSize newImageSize = CGSizeMake(self.size.width + borderWidth*2, self.size.height + borderWidth*2);
      UIGraphicsBeginImageContextWithOptions(newImageSize, NO, 0);
      [self drawInRect:CGRectMake(borderWidth, borderWidth, self.size.width, self.size.height)];
      UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      return newImage;
      }

  14. William K says:

    I am getting warnings with iOS 5.1: “Category is implementing a method which will also be implemented by its primary class”

    Any idea how to suppress this?

    Also, I think your library should be in github.

    Thanks!

    • Dirk says:

      Yes. Comment out this private method declarations in .m file:

      // Private helper methods
      /*
      @interface UIImage ()
      - (UIImage *)resizedImage:(CGSize)newSize
      transform:(CGAffineTransform)transform
      drawTransposed:(BOOL)transpose
      interpolationQuality:(CGInterpolationQuality)quality;
      - (CGAffineTransform)transformForOrientation:(CGSize)newSize;
      @end
      */

    • Jake Benilov says:

      I’ve created a gist from this code:

      https://gist.github.com/2009030

      • Rebecca says:

        I am getting a SIGBART error when I add this updated code. Can you provide some guidance?

        exception ‘NSInvalidArgumentException’, reason: ‘-[UIImage resizedImage:transform:drawTransposed:interpolationQuality:]: unrecognized selector sent to instance

  15. Jon Kean says:

    For anyone looking to crop an image correctly:

    - (UIImage *)croppedImage:(CGRect)newRect {
    newRect = CGRectIntegral(newRect);

    BOOL drawTransposed;

    switch (self.imageOrientation) {
    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored:
    drawTransposed = YES;
    break;
    default:
    drawTransposed = NO;
    }

    CGImageRef imageRef = self.CGImage;
    CGContextRef bitmap = CGBitmapContextCreate(NULL,
    (size_t)newRect.size.width,
    (size_t)newRect.size.height,
    CGImageGetBitsPerComponent(imageRef),
    0,
    CGImageGetColorSpace(imageRef),
    CGImageGetBitmapInfo(imageRef));
    if (bitmap == nil) {
    // wonky image, and can’t do anything in it’s current format.
    return nil;
    }

    CGRect clippedRect = CGRectMake(0, 0,
    drawTransposed ? newRect.size.height : newRect.size.width,
    drawTransposed ? newRect.size.width : newRect.size.height);
    CGContextClipToRect(bitmap, clippedRect);

    CGAffineTransform transform = [self transformForOrientation:newRect.size];
    CGContextConcatCTM(bitmap, transform);

    const CGSize imageSize = self.size;
    CGRect transposedRect = CGRectMake(-1*newRect.origin.y, -1*newRect.origin.x,
    imageSize.height, imageSize.width);
    CGRect drawRect = CGRectMake(-1*newRect.origin.x, -1*newRect.origin.y,
    imageSize.width, imageSize.height);
    CGContextDrawImage(bitmap, drawTransposed ? transposedRect : drawRect, imageRef);

    CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

    CGContextRelease(bitmap);
    CGImageRelease(newImageRef);

    return newImage;
    }

    // create duplicate image using a common colorspace and alpha format
    - (UIImage *) normalize {
    const CGSize size = self.size;
    CGColorSpaceRef genericColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef thumbBitmapCtxt = CGBitmapContextCreate(NULL,
    (size_t)size.width,
    (size_t)size.height,
    8,
    (size_t)(4 * size.width),
    genericColorSpace,
    kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(genericColorSpace);
    CGContextSetInterpolationQuality(thumbBitmapCtxt, kCGInterpolationDefault);
    CGRect destRect = CGRectMake(0, 0, size.width, size.height);
    CGContextDrawImage(thumbBitmapCtxt, destRect, self.CGImage);
    CGImageRef tmpThumbImage = CGBitmapContextCreateImage(thumbBitmapCtxt);
    CGContextRelease(thumbBitmapCtxt);
    UIImage *result = [UIImage imageWithCGImage:tmpThumbImage];
    CGImageRelease(tmpThumbImage);

    return result;
    }

  16. mamcx says:

    What about setup this in github or bitbucket instead?

  17. I’ve forked Trevor’s original code and put it up on GitHub for anyone who wants an updated copy. This fork includes a couple updates of my own, as well as the bug fixes written here in the comments.

    https://github.com/mbcharbonneau/UIImage-Categories

    I’ve been using this in my own iOS projects for about a year and it’s pretty stable AFAIK, but open an issue if you notice any bugs! I plan to keep it updated for future changes in the iOS SDK.

    • Todd Powers says:

      I used resizedImageWithContentMode: and when I used the Fill option, I expected it to return an image with the exact size that I specified, but it didn’t! In Trever’s explanation, it says “The larger dimension (in this case, the length) will be cropped”. No cropping was happening, so I added it to the end of the method.

      In place of the last line:
      return [self resizedImage:newSize interpolationQuality:quality];

      I put:

      UIImage* resizedImg = [self resizedImage:newSize interpolationQuality:quality];
      if (UIViewContentModeScaleAspectFill == contentMode && (newSize.height > bounds.height || newSize.width > bounds.width)) {
      return [resizedImg croppedImage:CGRectMake((newSize.width - bounds.width) / 2, (newSize.height - bounds.height) / 2, bounds.width, bounds.height)];
      }
      return resizedImg;

  18. [...] you’ve Googled about resizing or cropping UIImage objects, chances are you’ve read Trevor Harmon’s blog post on the subject. Trevor’s solution is great, but unfortunately a little dated since changes to the iOS SDK [...]

  19. Geometrico says:

    Strangely, when i use the resize method, i get this error :

    unrecognized selector sent to instance *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[UIImage resizedImage:interpolationQuality:]”

    there seems to be a problem with interpolationQuality. Here is how i call the method :

    UIImage *displayFinalImage = [_finalImage resizedImage:(_viewCanvas.frame.size) interpolationQuality:kCGInterpolationHigh];

    what am i doing wrong?

    • Alex says:

      I’ve copied the files to the project directory to resolve this issue. I’ve referenced the files from a folder before and it caused this error. My assumption is the folder path is not searched for the references…

  20. [...] table view. I would like the images in my table view to be resized and have round corners. I found UIImage+Resize and UIImage+RoundedCorner to do this. Both libraries works great seperately but I have not been abl to combine them. I could [...]

  21. Joe D'Andrea says:

    OK! Easy fixes. First, instead of @interface UIImage () you want @interface UIImage (PrivateRoundedCorner). Then it will compile without errors. The “Private” is because this is, literally, a private extension. Next, in UIImage+Resize.m, you will want to handle ALL UIImageOrientation values. Just break on the unused ones. That’s it!

  22. [...] Resize a UIImage the right way 源代码 [...]

  23. [...] are a few more files in the startup project. Inside “Categories,” you’ll find the UIImage categories from Trevor Harmon. You’ll need these to easily resize and crop images. As a side benefit, Trevor’s categories [...]

  24. [...] are a few more files in the startup project. Inside “Categories,” you’ll find the UIImage categories from Trevor Harmon. You’ll need these to easily resize and crop images. As a side benefit, Trevor’s categories [...]

  25. [...] Trevor Harmon for image resizing operations Nick Lockwood for file paths Andrew Sliwinski for async file storage [...]

  26. [...] 在启动项目中还有一些文件。在 Categories 中,你会发现来自 Trevor Harmon 的 UIImage categories。你需要这些来轻松地调整和裁剪图像。有个附带的好处就是,Trevor 的 [...]

  27. [...] 在启动项目中还有一些文件。在 Categories 中,你会发现来自 Trevor Harmon 的 UIImage categories。你需要这些来轻松地调整和裁剪图像。有个附带的好处就是,Trevor 的 [...]

  28. [...] Categories 中,你会发现来自 Trevor Harmon 的 UIImage categories。你需要这些来轻松地调整和裁剪图像。有个附带的好处就是,Trevor 的 [...]

  29. [...] you do want to do image resizing on the device, take a look at UIImage+Resize, a category made by this guy and doing that in the background queue will assure that it doesn’t lock up your [...]

  30. myr0 says:

    hi!

    i resized my image like this:

    resize = [image resizedImage:CGSizeMake(234, 172) interpolationQuality:1];

    and then i want to save it with NSFileManager:

    NSData *resizeData = UIImageJPEGRepresentation(resize,1.0);
    [fileManager createFileAtPath:resizePath contents:resizeData attributes:nil];

    right before that, i am saving another image the same way, that one is saved, but the second resized one causes a crash!

    any help here?

  31. Dan says:

    Can anyone help me with this. This is a great library, but I cant seem to figure out how to accomplish what I need to do, been trying for hours and just cant seem to get it.

    Basically, I am getting an image from the iOS camera, and then sending the image as a jpeg to a web server. When that image arrives at the we webserver, it must be upright, not rotated, and no matter what way the iOS device was held the image should be the same aspect ratio.

    Basically, I want the image to be a landscape image, so if the user takes a photo on the iOS camera, in landscape, it will just scale the image down. if the user takes a portrait photo, the code should look at them middle of the portrait image and extract a landscape sample from it.

    Any suggestions?
    Thank you

    - Running iOS 6, iPhone 5

  32. ailas says:

    Thanks for nice work and updates for iOS 6. It’s very useful.
    Cheers.
    Josef

  33. Yan says:

    Hi. Thank you for this great library. It works great. I just have a question about thumbnail function. I am creating thumbnails from people portraits and when the image is standard size i don’t have a problem but some images have a height is much greater then width and the thumbnail crops in the middle. In the documentation the you have a comment // The cropped rect must be centered on the resized image why is that and can that be changed? Would i be able to start the cropped rectangle at 0,0 or subtract some value from X when its to big to crop higher? Thanks again in advance!

  34. Fabian Frank says:

    This is a nice article. However, if you have a lot of images that you display, especially if a lot of them move in and out of view often, then processing the images (scaling, cropping) and then displaying them might fall short. CGContextDrawImage or UIImage drawInRect are quite expensive and occupy precious CPU time. You can mitigate the UI lag caused by that, by moving the resizing computation into the background. However, it then still delays the display of the image and occupies the CPU that could be used for other things. I found it more convenient to use a UIView, with an image property. Then setImage not only assigns the image to the class variable, but it also creates a CALayer, attaches the Image to it via layer.contents = (id) _image.CGImage; and adds this layer as a sub layer to self.layer. The image can be resized, by setting layer.frame and it can be cropped using self.layer.masksToBounds = YES.

    The cool thing about this solution is that it doesn’t require a custom drawRect method, therefore offering great performance “on the fly” resizing of images.

  35. Daniele C says:

    Hi,
    First of all, this article helped me a lot, but I would like to point out that there is a bug with 8bit PNG and your Resize class.

    test image:
    http://images.wikia.com/frontierville/images/8/86/Oak_Tree_Small-icon.png

    exception:
    CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaLast; 1024 bytes/row

    btw, thanks for the article

  36. Alex Hajdu says:

    What about the warnings ? Category is implementing a method which will also be implemented by its primary class?
    Is a good idea to use #pragma to remove warnings or it’s better way to make subclass rather a category to remove those warnings?

  37. Greg says:

    Great resize solution! I have been using Agile Uploader to allow clients to resize and then upload a smaller file size image to my website. works great with good quality but not for iPhone.

    Do you have a related solution I can host from my website.. then if a user logs onto my website from an iPhone, I can get them to a special photo upload page. I am thinking of using your code like a javascript applet download that the iphone user does not even have to mess with.. They just select the image they want to upload and it is automatically resized to my specs before being uploaded (all behind the scenes). 2M file knocked down to a few hundred K while maintaining solid quality.

    I don’t program on the iPhone yet so am limited there. Agile Uploader requires Flash which is anti-iphone. Any help or tips would be greatly appreciated.

    Also- I read somewhere that the iphone will automatically resize to 800×600 anyway? that may get me small enough if it ‘just does it’ for the upload.. I want to stay under 300K or so..
    -Greg

  38. [...] images, cropping them to a specific thumbnail size, applying rounded corners is extremely easy with Trevor Harmon’s UIImage categories. In fact, they proved to be invaluable to my latest project (there is not need [...]

  39. Motasem says:

    I made a UIImagePickerController with [picker setallowsediting;yes]; so i be able to crop the photo after i take it. But i just want the face to be cropped in that picture so i dont want the crop interface to be square, i want it to be circle.

  40. James True says:

    This could really use at least one example of how to use it. The resize method for example only takes a rect as input. How do you pass it in image?

  41. Hasan says:

    When I add these to a project I get several warnings that make me pause.

    “Category is implementing a method which will also be implemented by its primary class”

    When I look at SO I get an article like this: http://stackoverflow.com/questions/9424004/suppress-warning-category-is-implementing-a-method-which-will-also-be-implement

    It really feels like if you feel like a Category is the right way to do this job you aught to not act like a subclass, or alternately, simply be a subclass of UIImage.

    I really like the convenience of the classes, and appreciate the time and thought you have put into dealing with the *&^&^$%^ EXIF tags and CG Ref orientation madness…

    Oh, and one other small warning issue, You don’t fully enumerate the enum, and that is a warning now as well. I just added in cases and breaks to do nothing, but I didn’t feel clean when I was done…

  42. [...] that resizes UIImages accurately (and quickly) I recommend the UIImage categories described in this blog post. In general it is a lot quicker to use Quartz2D for a task like this because converting [...]

  43. [...] The image I have to return is not always 210×210 pixels, so I have to be able to change the size. Loading a “blank” PNG-File and manipulating it’s pixels was the only workaround until now I managed to get working. A more elegant way would be creating a blank Image with white BG of desired size I could use in my current method. Unfortunately I didn’t find anything on that subject yet. Only on manipulating pixels on allready existing Images. That’s how I came up with the current workaround. I allready tried the snipped for resizing from the following Link: Resizing a UIImage the right way [...]

  44. Lars says:

    Are these categories working correctly for iOS6? In my test app the image orientation is not correct after rescale crop.

    Using the code from this example:
    http://www.raywenderlich.com/13541/how-to-create-an-app-like-instagram-with-a-web-service-backend-part-22

    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];

    UIImage *scaledImage = [image resizedImageWithContentMode:UIViewContentModeScaleAspectFill bounds:CGSizeMake(self.imageView.frame.size.width, self.imageView.frame.size.height) interpolationQuality:kCGInterpolationHigh];

    UIImage *croppedImage = [scaledImage croppedImage:CGRectMake((scaledImage.size.width - self.imageView.frame.size.width)/2, (scaledImage.size.height - self.imageView.frame.size.height)/2, self.imageView.frame.size.width, self.imageView.frame.size.height)];

    self.imageView.image = croppedImage;

    With this code, the image ends up with the wrong orientation (if taken in portrait mode for example).

    If I set the image from the image picker directly, it has the correct orientation.

    Any tips, what am I doing it wrong?

  45. Lars says:

    To answer my own question:

    I used the code from GitHub which has the iOS5 rotation fix.
    This fix causes trouble on iOS6, removing it fixed the issue.

  46. Vishal says:

    I am trying to find how custom cropping can be achieved . I need a cropping layer above the image with aspect ratio of 7:9 this ratio should not change . I have tried PEPhotoCropEditor . Sorry i copy pasted whole project as i am new to iOS development . But i really want to learn how to achieve my goal . Please do reply ASAP .

  47. […] Great, we figure out what we need to do next is to rotate the image first, then  resize it. But how? There is a bunch of codes and articles in the internet which handle rotation and resize. The most famous one I know is http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/ […]

Leave a Reply