iPhone development – UIImage with round corners

UPDATE 2009-04-05
I’ve updated the code in the post and the sample project at the end of the post. Many thanks to Xavier Schott for his helpful comment!

I recently dwelled into the world of Cocoa programing for the iPhone a few weeks back. My good friend and collegue at Avantime, Hampus, and I bought iPhones 3G when we were on vaccation in Italy this summer. Italy sells unlocked iPhones so we’re not stuck with using Telia which is the only Swedish operator who carry iPhone. I actually had a quick look at iPhone development when the first iPhone came out using the toolchain but I didn’t really enjoy it at the time, perhaps it’s because I only owned an iPod touch at the time.

Anyhow. Hampus and I have been hacking away on our first iPhone app and I thought I’d share some usefull code for rounding off corners of an UIImage. This code is based on various stuff I found on google but I made a nice image manipulation class which rounds of corners:

ImageManipulation.h

#import 
 
@interface ImageManipulator : NSObject {
}
+(UIImage *)makeRoundCornerImage:(UIImage*)img :(int) cornerWidth :(int) cornerHeight;
@end

ImageManipulation.m

#import "ImageManipulator.h"
 
@implementation ImageManipulator
 
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight)
{
    float fw, fh;
    if (ovalWidth == 0 || ovalHeight == 0) {
        CGContextAddRect(context, rect);
        return;
    }
    CGContextSaveGState(context);
    CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGContextScaleCTM (context, ovalWidth, ovalHeight);
    fw = CGRectGetWidth (rect) / ovalWidth;
    fh = CGRectGetHeight (rect) / ovalHeight;
    CGContextMoveToPoint(context, fw, fh/2);
    CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
    CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
    CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
    CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
    CGContextClosePath(context);
    CGContextRestoreGState(context);
}
 
+(UIImage *)makeRoundCornerImage : (UIImage*) img : (int) cornerWidth : (int) cornerHeight
{
	UIImage * newImage = nil;
 
	if( nil != img)
	{
		NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
		int w = img.size.width;
		int h = img.size.height;
 
		CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
		CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
 
		CGContextBeginPath(context);
		CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
		addRoundedRectToPath(context, rect, cornerWidth, cornerHeight);
		CGContextClosePath(context);
		CGContextClip(context);
 
		CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
 
		CGImageRef imageMasked = CGBitmapContextCreateImage(context);
		CGContextRelease(context);
		CGColorSpaceRelease(colorSpace);
		[img release];
 
		newImage = [[UIImage imageWithCGImage:imageMasked] retain];
		CGImageRelease(imageMasked);
 
		[pool release];
	}
 
    return newImage;
}
 
@end

Just call the static method makeRoundCornerImage and pass your image to have the image rounded off the way you want.

Note that you do need the CoreGraphics framework for this to compile. By request I’ve also whipped up a small demo of how to use this. If you don’t like to copy-paste code you can grab the class files from the demo.

Download: UIImage round corners demo

47 thoughts on “iPhone development – UIImage with round corners

  1. Hi Björn.

    Thanks so much for the example. Would you be willing to indicate a license (such as, say, MIT) here or on the code itself. I and my employer would like to use the code, but still see the default “all rights reserved” Copyright notice in the downloaded code and I don’t see a copyright . We like to cover our bases with it comes to intellectual property.

    Cheers and thanks again,
    Jonathan

  2. Hi Jonathan,

    Of course you can use the code in which ever way you seem fit. I’m sorry about the “all rights reserved”, it’s just there because it’s the default output from XCode. I’m glad you find the code useful.

    If you do feel like you want to give something back, add a link to my blog if you have a site or blog etc of your own.

    Good luck on your project!
    Björn

  3. I find that I generally have more control over when data is released if I do not let anything linger in the NSAutoReleasePool. In the long run, this seems to make products more stable and crash less unexpectedly upon [ release ].

    I suggest to surround the +makeRoundCornerImage source code above as follow:

    {
    UIImage * newImage = nil;

    if( nil != img)
    {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    int w = img.size.width;
    int h = img.size.height;
    ...

    and

    ...
    [img release];

    newImage = [[UIImage imageWithCGImage:imageMasked] retain];
    [ pool release];
    }

    return newImage;
    }

    This will have the general effect to put the returned image in the same -retain state as the original, and allow invocation like this:

    UIImage* image = [[UIImage alloc] initWithData:some_data]; // <- retained
    image = [ ImageManipulator makeRoundCornerImage:image :7 :7 ]; // <- released, then retained again

    Thank you for this post.
    Xavier

  4. I seem to have run into a problem when trying to use this function more than once. In the demo, I added the following lines to to ViewDidLoad in RoundCornerDemoViewController.m. Specifically I added them on line 22 immediately after two similar lines.

    UIImage *imageFromFilesystem2 = [UIImage imageNamed:@"DavidHasselhoff.png"];

    UIImage *imageWithRoundCorners2 = [ImageManipulator makeRoundCornerImage:imageFromFilesystem2 : 20 : 20];

    In turn I get this run-time error:

    objc[62664]: FREED(id): message release sent to freed object=0x522df0
    Program received signal: “EXC_BAD_INSTRUCTION”.

    Unable to disassemble std::__timepunct_cache::_S_timezones.

  5. Lastly, the generated images seems to be slightly blurry for me. Any suggestions on how to maintain image quality?

  6. Hi Brandon!

    I don’t know if there is any way improve or preserve the image quality but I haven’t noticed any blurring etc either. Are you sure you’re not stretching or scaling the image in the UIImageView?

  7. I have notices that this method eats my memory… If I call for several times the memory will grow and grow and I can’t find why

  8. I finally figured out… you have to release imageMasked. You did create it but you did not relase it. Add this line before the return on your method:
    CGImageRelease(imageMasked);

  9. Hey, thanks for the code. Worked great with my own image. Im now trying to get it to work with the image that is returned from the UIImagePicker delegate (when you incorporate UIImagePickerController and user picks a photo from library or whatever). It displays the returned rounded image in my UIImageView correctly for like a second and then crashes immediately.
    This sample code you provided should work in that scenario, no?

    I’m doing it like this….
    -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo {

    [self useImage:image];
    [[picker parentViewController] dismissModalViewControllerAnimated:YES];
    [picker release];
    }

    - (void)useImage:(UIImage*)img
    {
    UIImage *final = [ImageManipulator makeRoundCornerImage:img :15 :15];
    [pictureView setImage:final];
    }
    if I do this same thing but with an image from the Resources bundle, everything is fine…with and without rounded corners

  10. God dag! Kan jag ladda ner en bild fran din blogg. Av sak med hanvisning till din webbplats!

  11. Thanks so much for your excellent work!
    Something small I changed is the name of your method, so it’s a bit more consistent with the default cocoa method naming conventions:
    +(UIImage *)makeRoundCornerImageFromImage:(UIImage *)img withCornerWidth:(int)cornerWidth andCornerHeight:(int)cornerHeight;

    now you can say:
    imageFromFile = [ImageManipulator makeRoundCornerImageFromImage:imageFromFile withCornerWidth:10 andCornerHeight:10];

    instead of just the numbers like
    imageFromFile = [ImageManipulator makeRoundCornerImageFromImage:imageFromFile :10 :10];

    Cheers!

  12. Nice work, but you _must_ remove the [img release] from that method. You do not own that reference and should not be releasing it. This _will_ cause crashes from over releasing objects. For example, this code, which follow Objective-C best practices will crash due to your code releasing an object it does not own:

    UIImage *image = [[UIImage alloc] initWithData:someData] ;
    UIImage *roundedImage = [ImageManipulator makeRoundCornerImage:image :10 :10];
    myImageView.image = roundedImage;
    [roundedImage release];
    [image release];

  13. I will second what Michael said, nice work, but that release of the passed in image has to go. Just spent a while tracking down how my image became a zombie.

  14. Fantastic work! How would you go about rounding off just one corner? E.g. for an imageview in a groupedstyle tableview.

  15. I thought this code was great but does it work with a number of images? I have an image for each cell on my tableview. When I open the view the images are all rounded nicely but when I scroll the app crashes.

  16. Here’s another way to handle it using a UIImageView (into which you place said image) … and (drum roll) layers!

    #import

    // Add a corner radius:
    CALayer *l = [myImageView layer];
    l.masksToBounds = YES;
    l.cornerRadius = 10.0;
    l.borderWidth = 1.0;
    l.borderColor = [[UIColor darkGrayColor] CGColor];

    I kid you not. Works like a charm.

    Be sure to include the QuartzCore framework, as well as the header up top in your implementation file.

  17. Of course, messing with the CALayer of a UIImageView might not be such a good idea after all. (Try scrolling with one of these image views inside.) Hmm. Well, maybe there’s a happy medium that’s just as minimal, code-wise, but still gets what we want in short order (?).

  18. OK, next best thing … but you need to be using Joe Hewitt’s Three20 library. (I know, cheating, right? Ahh well.) This example gives your TTImageView rounded corners. I gather you could also chain another style at the end to get the border.

    i.backgroundColor = [UIColor clearColor];
    i.style = [TTShapeStyle styleWithShape:[TTRoundedRectangleShape shapeWithTopLeft:10 topRight:10 bottomRight:10 bottomLeft:10] next:[TTContentStyle styleWithNext:nil]];

  19. super easy:

    #import

    appIconImage.image = [UIImage imageWithContentsOfFile:@"image.png"];
    appIconImage.layer.masksToBounds = YES;
    appIconImage.layer.cornerRadius = 10.0;
    appIconImage.layer.borderWidth = 1.0;
    appIconImage.layer.borderColor = [[UIColor grayColor] CGColor];

  20. The analyzer is still going to think the image is a zombie, so return the image passed in:

    +(UIImage *)makeRoundCornerImage : (UIImage*) img : (int) cornerWidth : (int) cornerHeight
    {
    UIImage * newImage = nil;

    if( nil != img) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int w = img.size.width;
    int h = img.size.height;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGContextBeginPath(context);
    CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
    addRoundedRectToPath(context, rect, cornerWidth, cornerHeight);
    CGContextClosePath(context);
    CGContextClip(context);

    CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);

    CGImageRef imageMasked = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    newImage = [[UIImage imageWithCGImage:imageMasked] retain];
    CGImageRelease(imageMasked);
    img = newImage;
    [newImage release];
    [pool release];
    }
    return img;
    }

    @end

  21. Hei Björn, Excellent, thank you very much indeed!

    @Michael: “Nice work, but you _must_ remove the [img release] from that method.”

    Thank you for this important hint, the scrolling speed in a table view without the [img release] is much (!) higher.

  22. The file works well however in some instances the image corners are black. Since my tableviewcell is white, you can see the black corners. In other instances, the corners are transparent. The black is drawn in a drawRect method. Would that make a difference? What needs to be changed in your class to guarantee clear corners?

  23. Why you dont use the layer?

    imageView.layer.cornerRadius = 5.0;
    imageView.layer.masksToBounds = YES;

    eBuildy, french iPhone specialits

  24. Is there really anybody out there that believes they are giving away free ipods on facebook? lol I am also very scared that a masked man is going to kill me if I don’t send out that chain letter.

  25. Hello! Followup question: Anyone have ideas on how to make this Retina display friendly? (Certainly an imperative at this point in time.) So far, I end up with 1x-equivalent renderings. I hope it’s somehow pilot error but all indicators suggest it isn’t. :(

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>