Rounded Corners on UIImage

IosObjective CIphoneCocoa TouchImage Manipulation

Ios Problem Overview


I'm trying to draw images on the iPhone using with rounded corners, a la the contact images in the Contacts app. I've got code that generally work, but it occasionally crashes inside of the UIImage drawing routines with an EXEC_BAD_ACCESS - KERN_INVALID_ADDRESS. I thought this might be related to the cropping question I asked a few weeks back, but I believe I'm setting up the clipping path correctly.

Here's the code I'm using - when it doesn't crash, the result looks fine and anybody looking to get a similar look is free to borrow the code.

- (UIImage *)borderedImageWithRect: (CGRect)dstRect radius:(CGFloat)radius {
    UIImage *maskedImage = nil;
    
    radius = MIN(radius, .5 * MIN(CGRectGetWidth(dstRect), CGRectGetHeight(dstRect)));
    CGRect interiorRect = CGRectInset(dstRect, radius, radius);
    
    UIGraphicsBeginImageContext(dstRect.size);
    CGContextRef maskedContextRef = UIGraphicsGetCurrentContext();
    CGContextSaveGState(maskedContextRef);
    
    CGMutablePathRef borderPath = CGPathCreateMutable();
    CGPathAddArc(borderPath, NULL, CGRectGetMinX(interiorRect), CGRectGetMinY(interiorRect), radius, PNDegreeToRadian(180), PNDegreeToRadian(270), NO);
    CGPathAddArc(borderPath, NULL, CGRectGetMaxX(interiorRect), CGRectGetMinY(interiorRect), radius, PNDegreeToRadian(270.0), PNDegreeToRadian(360.0), NO);
    CGPathAddArc(borderPath, NULL, CGRectGetMaxX(interiorRect), CGRectGetMaxY(interiorRect), radius, PNDegreeToRadian(0.0), PNDegreeToRadian(90.0), NO);
    CGPathAddArc(borderPath, NULL, CGRectGetMinX(interiorRect), CGRectGetMaxY(interiorRect), radius, PNDegreeToRadian(90.0), PNDegreeToRadian(180.0), NO);
    
    CGContextBeginPath(maskedContextRef);
    CGContextAddPath(maskedContextRef, borderPath);
    CGContextClosePath(maskedContextRef);
    CGContextClip(maskedContextRef);
    
    [self drawInRect: dstRect];

    maskedImage = UIGraphicsGetImageFromCurrentImageContext();
    CGContextRestoreGState(maskedContextRef);
    UIGraphicsEndImageContext();
    
    return maskedImage;
}

and here's the crash log. It looks the same whenever I get one of these crashes

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x6e2e6181
Crashed Thread:  0

Thread 0 Crashed: 0 com.apple.CoreGraphics 0x30fe56d8 CGGStateGetRenderingIntent + 4 1 libRIP.A.dylib 0x33c4a7d8 ripc_RenderImage + 104 2 libRIP.A.dylib 0x33c51868 ripc_DrawImage + 3860 3 com.apple.CoreGraphics 0x30fecad4 CGContextDelegateDrawImage + 80 4 com.apple.CoreGraphics 0x30feca40 CGContextDrawImage + 368 5 UIKit 0x30a6a708 -[UIImage drawInRect:blendMode:alpha:] + 1460 6 UIKit 0x30a66904 -[UIImage drawInRect:] + 72 7 MyApp 0x0003f8a8 -[UIImage(PNAdditions) borderedImageWithRect:radius:] (UIImage+PNAdditions.m:187)

Ios Solutions


Solution 1 - Ios

Here is an even easier method that is available in iPhone 3.0 and up. Every View-based object has an associated layer. Each layer can have a corner radius set, this will give you just what you want:

UIImageView * roundedView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:@"wood.jpg"]];
// Get the Layer of any view
CALayer * l = [roundedView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:10.0];

// You can even add a border
[l setBorderWidth:4.0];
[l setBorderColor:[[UIColor blueColor] CGColor]];

Solution 2 - Ios

I'm gonna go ahead here and actually answer the question in the title.

Try this category.

UIImage+additions.h

#import <UIKit/UIKit.h>

@interface UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS;
@end

UIImage+additions.m

#import "UIImage+additions.h"

@implementation UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS {
	UIImage *image = self;

	// Begin a new image that will be the new image with the rounded corners
	// (here with the size of an UIImageView)
	UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);

	const CGRect RECT = CGRectMake(0, 0, image.size.width, image.size.height);
	// Add a clip before drawing anything, in the shape of an rounded rect
	[[UIBezierPath bezierPathWithRoundedRect:RECT cornerRadius:RADIUS] addClip];
	// Draw your image
	[image drawInRect:RECT];

	// Get the image, here setting the UIImageView image
	//imageView.image
	UIImage* imageNew = UIGraphicsGetImageFromCurrentImageContext();

	// Lets forget about that we were drawing
	UIGraphicsEndImageContext();

	return imageNew;
}
@end

Solution 3 - Ios

If appIconImage is an UIImageView, then:

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];

And also remember:

#import <QuartzCore/QuartzCore.h>

Solution 4 - Ios

I cant offer any insight into your crash, but I thought I would offer another option for rounding the corners. I had a similar problem arise in an application i was working on. Rather than write any code I am overlaying another image which masks off the corners.

Solution 5 - Ios

If you are calling your method (borderedImageWithRect) in a background thread, crashes might occur since UIGraphics-functions are not thread-safe. In such a case, you must create a context using CGBitmapContextCreate() - see the "Reflection" sample code from the SDK.

Solution 6 - Ios

The easiest way is to embed a disabled[!] round-rect [not custom!] button in your view (can even do it all in the Interface Builder) and associate your image with it. The image-setting message is different for UIButton (compared to UIImageView), but the overall kludge works like a charm. Use setImage:forState: if you want a centered icon or setBackgroundImage:forState: if you want the whole image with corners cut (like Contacts). Of course if you want to display lots of these images in your drawRect this isn't the right approach, but more likely an embedded view is exactly what you needed anyway...

Solution 7 - Ios

I would reiterate fjoachim's answer: be cautious when attempting to draw while running on a separate thread, or you may get EXC_BAD_ACCESS errors.

My workaround went something like this:

UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"] 
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];

(In my case I was resizing / scaling UIImages.)

Solution 8 - Ios

I actually had a chance to talk about this with somebody from Apple at the iPhone Tech Talk in New York. When we talked about it, he was pretty sure it wasn't a threading issued. Instead, he thought that I needed to retain the graphics context that was generated when calling UIGraphicsBeginImageContext. This seems to violate the general rules dealing with retain rules and naming schemes, but this fellow was pretty sure he'd seen the issue previously.

If the memory was getting scribbled, perhaps by another thread, that would certainly explain why I was only seeing the issue occasionally.

I haven't had time to revisit the code and test out the fix, but PCheese's comment made me realize I hadn't posted the info here.

...unless I wrote that down wrong and UIGraphicsBeginImageContext should've been CGBitmapContextCreate...

Solution 9 - Ios

If it only crashes some of the time, figure out what the crash cases have in common. Is dstRect the same every time? Are the images ever a different size?

Also, you need to CGPathRelease(borderPath), although I doubt that leak is causing your problem.

Solution 10 - Ios

UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"] 
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];

Solution 11 - Ios

In Swift 4.2 and Xcode 10.1

let imgView = UIImageView()
imgView.frame = CGRect(x: 200, y: 200, width: 200, height: 200)
imgView.image = UIImage(named: "yourimagename")
imgView.imgViewCorners()
//If you want complete round shape
//imgView.imgViewCorners(width: imgView.frame.width)//Pass ImageView width
view.addSubview(imgView)

extension UIImageView {
//If you want only round corners
func imgViewCorners() {
    layer.cornerRadius = 10
    layer.borderWidth = 1.0
    layer.borderColor = UIColor.red.cgColor
    layer.masksToBounds = true
}
//If you want complete round shape
func imgViewCorners(width:CGFloat) {
    layer.cornerRadius = width/2
    layer.borderWidth = 1.0
    layer.borderColor = UIColor.red.cgColor
    layer.masksToBounds = true
}

Solution 12 - Ios

Set the Image in xib or storyboard (image width and height 41x41).

FirstViewController.h

@interface....

IBOutlet UIImageView *testImg;

@end

FirstViewController.m

-(void)viewDidLoad{
        testImg.layer.backgroundColor=[[UIColor clearColor] CGColor];
        testImg.layer.cornerRadius=20;
        testImg.layer.masksToBounds = YES;
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJablairView Question on Stackoverflow
Solution 1 - IosMagicSethView Answer on Stackoverflow
Solution 2 - IosJonnyView Answer on Stackoverflow
Solution 3 - IoscuasijoeView Answer on Stackoverflow
Solution 4 - IosLoungesView Answer on Stackoverflow
Solution 5 - IosfjoachimView Answer on Stackoverflow
Solution 6 - IosMax MotovilovView Answer on Stackoverflow
Solution 7 - IosPCheeseView Answer on Stackoverflow
Solution 8 - IosJablairView Answer on Stackoverflow
Solution 9 - IosbenzadoView Answer on Stackoverflow
Solution 10 - Iosuser3536010View Answer on Stackoverflow
Solution 11 - IosNareshView Answer on Stackoverflow
Solution 12 - Iosuser3774495View Answer on Stackoverflow