Code, Code, Revolution!
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
With this blog I try to provide useful tips and solutions for programming .NET, Objective-C and more. My name is Björn Sållarp, and I love writing code.
iPhone development - Downloading an image asynchronously
October 1st, 2008 at 11:14 am
[...] development – Downloading an image asynchronously Oct.01, 2008 in Development, iPhone In a recent post I showed you how to round of edges of an image. But how can you go about downloading images from [...]
vj
December 19th, 2008 at 11:19 am
can you show example using makeRoundCornerImage function? as i’m having problems
thx
Björn Sållarp
December 20th, 2008 at 4:18 am
Hi Vj,
I’ve updated the post. You’ll find a demo project at the end of the post. Hope it solves your problem.
Happy christmas coding!
// Björn Sållarp
vj
December 21st, 2008 at 1:05 am
Thanks Björn
Portion of Image as Button Background - iPhone Dev SDK Forum
January 27th, 2009 at 3:45 am
[...] this one helped me get starting iPhone development – UIImage with round corners [...]
Jonathan
February 1st, 2009 at 2:37 pm
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
Björn Sållarp
February 2nd, 2009 at 5:22 am
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
Xavier Schott
February 12th, 2009 at 10:06 am
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
DEV» Blog Archive » Drowning in NSAutoreleasePool
February 13th, 2009 at 4:26 am
[...] An example of this technique can be found here. [...]
Brandon
February 24th, 2009 at 1:36 am
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=0×522df0
Program received signal: “EXC_BAD_INSTRUCTION”.
Unable to disassemble std::__timepunct_cache::_S_timezones.
Brandon
February 24th, 2009 at 1:43 am
Xavier Schott’s change does fix the issue I just mentioned.
Brandon
February 24th, 2009 at 1:45 am
Lastly, the generated images seems to be slightly blurry for me. Any suggestions on how to maintain image quality?
Björn Sållarp
February 24th, 2009 at 5:19 am
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?
acalatrava
April 4th, 2009 at 2:49 pm
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
acalatrava
April 4th, 2009 at 2:54 pm
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);
Some more iPhone SDK tips and tricks | www.richardhyland.com ..::.. richard's diary
April 8th, 2009 at 9:07 am
[...] first I found on a blog and it works brilliantly. To make rounded corners on any [...]
Frank
April 14th, 2009 at 2:49 am
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
ChabrellIgan
April 20th, 2009 at 12:37 pm
God dag! Kan jag ladda ner en bild fran din blogg. Av sak med hanvisning till din webbplats!
Björn Sållarp
April 20th, 2009 at 4:10 pm
Sure, go ahead.
Pieter Dhondt
May 12th, 2009 at 10:38 am
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!
Long Haired Skippy
May 19th, 2009 at 12:38 am
This is awesome, thanks for sharing, it works like a charm.
UIImage Rounded Corners | kwigbo
May 27th, 2009 at 5:00 pm
[...] UIImage Rounded corners No TweetBacks yet. (Be the first to Tweet this post) Categories: Classes, iPhone SDK Tags: Comments (0) Trackbacks (0) Leave a comment Trackback [...]
Michael
July 22nd, 2009 at 1:32 am
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];
Guy Umbright
September 29th, 2009 at 4:10 pm
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.
Resize a UIImage the right way — Trevor’s Bike Shed
October 13th, 2009 at 8:01 am
[...] 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. [...]
DJ
October 16th, 2009 at 4:39 pm
Fantastic work! How would you go about rounding off just one corner? E.g. for an imageview in a groupedstyle tableview.
Stephen
November 7th, 2009 at 8:31 pm
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.
DEV» Blog Archive » Drowning in NSAutoreleasePool
November 14th, 2009 at 2:59 am
[...] An example of this technique can be found on Björn Sållarp’s article “UIImage with round corners”: http://blog.sallarp.com/iphone-uiimage-round-corners [...]
Joe
November 21st, 2009 at 3:38 am
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.
Joe
November 21st, 2009 at 3:58 am
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 (?).
Joe
November 21st, 2009 at 4:14 am
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]];
sabes
December 9th, 2009 at 5:07 am
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];
Cole
January 29th, 2010 at 11:02 pm
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
haptic
March 20th, 2010 at 11:02 pm
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.
Ben Kim
April 10th, 2010 at 5:10 pm
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?
eBuildy
June 21st, 2010 at 1:13 am
Why you dont use the layer?
imageView.layer.cornerRadius = 5.0;
imageView.layer.masksToBounds = YES;
eBuildy, french iPhone specialits
tdog09
June 28th, 2010 at 11:12 pm
much obliged, this helps ALOT!
Joe
June 29th, 2010 at 1:57 pm
eBuildy, that works too (and for any UIView to boot). Beware though, I hear it’s relatively slow – accent on relatively – so if you want rounded corner images in a UITableView, the scrolling might get a bit chunky.
http://three20.pypt.lt/uiimageview-rounded-corners