UIImage with transparent rounded corners - iphone

I'm using following code to add rounded corners to my UIImage, but the problem is that the rounded corners are showing "white" area instead of transparent or "clear". What am i doing wrong here:
- (UIImage *)makeRoundCornerImageWithCornerWidth:(int)cornerWidth cornerHeight:(int)cornerHeight {
UIImage * newImage = nil;
if (self != nil) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int w = self.size.width;
int h = self.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
[self addRoundedRectToPath:context rect:rect width:cornerWidth height:cornerHeight];
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), self.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
newImage = [[UIImage imageWithCGImage:imageMasked] retain];
CGImageRelease(imageMasked);
[pool release];
}
return [newImage autorelease];
}
- (void)addRoundedRectToPath:(CGContextRef)context rect:(CGRect)rect width:(float)ovalWidth height:(float)ovalHeight {
float fw, fh;
// If the width or height of the corner oval is zero, then it reduces to a right angle,
// so instead of a rounded rectangle we have an ordinary one.
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
// Save the context's state so that the translate and scale can be undone with a call
// to CGContextRestoreGState.
CGContextSaveGState(context);
// Translate the origin of the contex to the lower left corner of the rectangle.
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
//Normalize the scale of the context so that the width and height of the arcs are 1.0
CGContextScaleCTM (context, ovalWidth, ovalHeight);
// Calculate the width and height of the rectangle in the new coordinate system.
fw = CGRectGetWidth (rect) / ovalWidth;
fh = CGRectGetHeight (rect) / ovalHeight;
// CGContextAddArcToPoint adds an arc of a circle to the context's path (creating the rounded
// corners). It also adds a line from the path's last point to the begining of the arc, making
// the sides of the rectangle.
CGContextMoveToPoint(context, fw, fh/2); // Start at lower right corner
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); // Top right corner
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); // Top left corner
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); // Lower left corner
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // Back to lower right
// Close the path
CGContextClosePath(context);
// Restore the context's state. This removes the translation and scaling
// but leaves the path, since the path is not part of the graphics state.
CGContextRestoreGState(context);
}

Here's a simpler formulation using UIKit calls:
- (UIImage*) roundCorneredImage: (UIImage*) orig radius:(CGFloat) r {
UIGraphicsBeginImageContextWithOptions(orig.size, NO, 0);
[[UIBezierPath bezierPathWithRoundedRect:(CGRect){CGPointZero, orig.size}
cornerRadius:r] addClip];
[orig drawInRect:(CGRect){CGPointZero, orig.size}];
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Notice the NO parameter - this makes the image context transparent, so the clipped-out region is transparent.

https://github.com/detroit-labs/AmazeKit
sounds like a job for a library

Right after creating the bitmap context clear it with:
CGContextClearRect (context, CGRectMake(0, 0, w, h));

lukya's comment below your question is what you probably want to do.
Make sure you import QuartzCore:
#import <QuartzCore/QuartzCore.h>
Then, if you have a UIImageView of your image that you want to have rounded corners, just call (assuming imageView is a property and cornerRadius is the desired corner radius):
self.imageView.layer.cornerRadius = cornerRadius;
self.imageView.clipsToBounds = YES;
Since you already have self.CGImage, you could do this to create a UIImageView:
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:self.CGImage]];
Just make sure to release the imageView after you add it as a subview.

profileImageView.layer.cornerRadius = profileImageView.frame.size.height/2;
profileImageView.clipsToBounds = YES;

Related

Quartz UIImage with rounded corners and transparent background on iOS

I have used this solution to apply corner radius to UIImage in Quartz. It works fine like I predicted.
However the corners area outside of the image are not transparent but somewhat colored in white:
The image above shows top left corner of processed image that is opaque and not transparent.
I want cropped-out edges to be totally transparent. How can I achieve this?
EDIT: If I want to do this with UIImageView I would already done this. So please keep in mind that I want to do this with Quartz on UIImage object not UIImageView.
SOLUTION: The problem is actually not in the drawing code but rather in writing that image to the file system. I've saved image as JPEG an not as PNG. That's why corners were not transparent cos JPEG does not have alpha filter.
I usually use a custom category implemented on UIImage to crop the images with round corners. This was taken from this answer.
void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight, BOOL top, BOOL bottom)
{
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, 0);
NSLog(#"bottom? %d", bottom);
if (top) {
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 3);
} else {
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 0);
}
if (bottom) {
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 3);
} else {
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 0);
}
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 0);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
- (UIImage *)roundCornersOfImage:(UIImage *)source roundTop:(BOOL)top roundBottom:(BOOL)bottom {
int w = source.size.width;
int h = source.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, w, h);
addRoundedRectToPath(context, rect, 4, 4, top, bottom);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), source.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return [UIImage imageWithCGImage:imageMasked];
}
add these bellow lines in your drawRect method ..
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
///your another code here
}
after create Context it required to clear another context from that Rect... so after that line add this line CGContextClearRect(context, rect);.
For transparent use this:
const float colorMasking[6] = {1.0, 1.0, 0.0, 0.0, 1.0, 1.0};
yourImage = [UIImage imageWithCGImage: CGImageCreateWithMaskingColors(yourImage.CGImage, colorMasking)];
EDIT : Add this both line in method of UIImage category
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
The problem is actually not in the drawing code but rather in writing that image to the file system. I've saved image as JPEG an not as PNG. That's why corners were not transparent cos JPEG does not have alpha filter.
Just replaced this line of code:
[UIImageJPEGRepresentation(image, 0.75) writeToFile:path atomically:YES];
... with this one:
[UIImagePNGRepresentation(image) writeToFile:path atomically:YES];
Try this code
Image.layer.cornerRadius = 10.0;
Image.layer.borderColor = [UIColor blackColor].CGColor;
Image.layer.borderWidth = 0.5;
Image.clipsToBounds = YES;
Image.backgroundColor = [UIColor clearColor];
Hope it helps you
EDIT :-
I think you just need to add this :-
Image.clipsToBounds = YES;

drawRect at double the size

If I override drawRect in order to display an image and place a dinamically-generated overlay on it (see code), whenever I scale up the image it is drawn in a very blurry way as the result of the scaling.
The image is composed of two pieces, an image drawn from a png (whose original size is 2x the wanted one, so it should not give problems when scaled, but it does) and the other is dinamically generated according to the rect size, so it should also adapt to the current rect size, but it doesn't.
Any help?
- (void) drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextDrawImage(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height), [UIImage imageNamed:#"actionBg.png"].CGImage);
// generate the overlay
if ([self isActive] == NO && self.fullDelay != 0) { // TODO: remove fullDelay check!
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
CGContextRef overlayCtx = UIGraphicsGetCurrentContext();
int segmentSize = (rect.size.height / [self fullDelay]);
for (int i=0; i<[self fullDelay]; i++) {
float alpha = 0.9 - (([self fullDelay] * 0.1) - (i * 0.1));
[[UIColor colorWithRed:120.0/255.0 green:14.0/255.0 blue:14.0/255.0 alpha:alpha] setFill];
if (currentDelay > i) {
CGRect r = CGRectMake(0, i * segmentSize, rect.size.width, segmentSize);
CGContextFillRect(overlayCtx, r);
}
[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.3] setFill];
CGRect line = CGRectMake(0, (i * segmentSize) + segmentSize - 1 , rect.size.width, 1);
CGContextFillRect(overlayCtx, line);
}
UIImage *overlay = UIGraphicsGetImageFromCurrentImageContext();
UIImage *overlayMasked = [TDUtilities maskImage:overlay withMask:[UIImage imageNamed:#"actionMask.png"]];
// prevent the drawings to be flipped
CGContextTranslateCTM(overlayCtx, 0, rect.size.height);
CGContextScaleCTM(overlayCtx, 1.0, -1.0);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextDrawImage(ctx, rect, overlayMasked.CGImage);
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
UIGraphicsEndImageContext();
}
The problem is that you are drawing overlayMasked as a CGImage with CGContextDrawImage, which knows nothing of scale. Either you will have to double the size yourself manually if you're in a double-scale situation, or you should use UIImage, which knows about scale.

How to round edges of an UIImage?

I try to speed up the scrolling of my UITableView. I do this by do the drawing of the cells by myself instead of adding subviews.
One of the things I want to draw is an image. The image should have rounded edges. When I drew the cell using subviews I altered the layer of the UIImageView to have round corners.
Now I draw the UIImage directly and does not have a layer to modify. How can I draw the image with round edges?
I'm pretty sure I got this code from stackoverflow.
- (UIImage*)maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
CGImageRef maskRef = maskImage.CGImage;
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);
CGImageRef sourceImage = [image CGImage];
CGImageRef imageWithAlpha = sourceImage;
//add alpha channel for images that don't have one (ie GIF, JPEG, etc...)
//this however has a computational cost
// needed to comment out this check. Some images were reporting that they
// had an alpha channel when they didn't! So we always create the channel.
// It isn't expected that the wheelin application will be doing this a lot so
// the computational cost isn't onerous.
//if (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone) {
imageWithAlpha = CopyImageAndAddAlphaChannel(sourceImage);
//}
CGImageRef masked = CGImageCreateWithMask(imageWithAlpha, mask);
CGImageRelease(mask);
//release imageWithAlpha if it was created by CopyImageAndAddAlphaChannel
if (sourceImage != imageWithAlpha) {
CGImageRelease(imageWithAlpha);
}
UIImage* retImage = [UIImage imageWithCGImage:masked];
CGImageRelease(masked);
return retImage;
}
and I call it with:
customImage = [customImage maskImage:customImage withMask:[UIImage imageNamed:#"CircularMask.png"]];
[customImageView setImage:customImage];
Hope that helps!
It sounds like you simply want to remove the corners from a rectangular image: Create a new image via CGImage APIs -- you will apply a mask your input image.
Do you use caching for the images?
If so I'd recommend that you apply this to the UIImage before you cache it so once it's been drawn with rounded corners you can reuse that image.
Here's a category on UIImage that you can use to get a rounded UIImage.
I might have found this code somewhere so I apologize in advance to the original author for not giving him/her a credit.
#import <Foundation/Foundation.h>
#interface UIImage (DPRounded)
- (UIImage *)imageWithCornerRadius:(CGFloat)radius;
#end
#implementation UIImage (DPRounded)
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 *)imageWithCornerRadius:(CGFloat)radius
{
UIImage * newImage = nil;
if(self != nil)
{
int w = self.size.width;
int h = self.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
addRoundedRectToPath(context, rect, radius, radius);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), self.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
newImage = [[UIImage imageWithCGImage:imageMasked] retain];
CGImageRelease(imageMasked);
}
return [newImage autorelease];
}
#end
(Before you embark on this, please check that you're dequeueing table view cells correctly. This will do a lot to improve performance.)
Draw the image yourself with rounded corners and have the background of the cell be transparent, say.
If your cells are of variable height,
Use resizableImageWithCapInsets: (iOS5.0 and up), or the deprecated stretchableImageWithLeftCapWidth:topCapHeight: for earlier iOSes, to return an image that will expand by repeating a central block of your image. This will also protect the curvature of the corners when autoresizing is carried out. Or,
Cut up your image into pieces and have separate UIImageView instances for each piece and set autoresizing appropriately for each piece. This is best option as far as performance goes, but you might end up with as many as nine outlets.

Draw a rounded UIView with gradient and drop shadow

EDIT:
I finally found a real simple solution to this problem, using the CAGradientLayer class, and the CALayer drawing functionalities.
Ole Begemann released a great UIView wrapper for CAGradientLayer class named OBGradientView.
This class allows you to easily create a gradient UIView in your application.
You then use the CALayer drawing functionalities to add the rounded corners and drop shadow values :
// Create the gradient view
OBGradientView *gradient = [[OBGradientView alloc] initWithFrame:someRect];
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor yellowColor], nil];
gradient.colors = colors;
// Set rounded corners and drop shadow
gradient.layer.cornerRadius = 5.0;
gradient.layer.shadowColor = [UIColor grayColor].CGColor;
gradient.layer.shadowOpacity = 1.0;
gradient.layer.shadowOffset = CGSizeMake(2.0, 2.0);
gradient.layer.shadowRadius = 3.0;
[self.view addSubview:gradient];
[gradient release];
Dont forget to add the QuartzCore framework to your project.
ORIGINAL QUESTION:
I have been working on a custom control that is a rounded rectangle button, filled with a linear gradient, and having a drop shadow.
I have filled the two first steps using this answer : link text
My problem is now to add a drop shadow under the resulting shape.
Actually, the context has been clipped to the rounded rect path, so when I use the CGContextSetShadow function, it doesn't draw it.
I tried to solve this problem by drawing the rounded rect twice, first with a plain color, so it draws the shadow, and then redraw it with the gradient fill.
It kinda worked, but I still can see a few pixels at the corners of the shape resulting from the first draw with a plain color, as you can see on this zoomed version :
http://img269.imageshack.us/img269/6489/capturedcran20100701192.png
It is almost good, but not perfect yet...
Here is my -drawRect: implementation :
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);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGSize shadowOffset = CGSizeMake(10.0, 10.0);
CGFloat blur = 5.0;
rect.size.width -= shadowOffset.width + blur;
rect.size.height -= shadowOffset.height + blur;
CGContextSaveGState(context);
addRoundedRectToPath(context, rect, _radius, _radius);
CGContextSetShadow (context, shadowOffset, blur);
CGContextDrawPath(context, kCGPathFill);
CGContextRestoreGState(context);
addRoundedRectToPath(context, rect, _radius, _radius);
CGContextClip(context);
CGFloat colors[] =
{
_gradientStartColor.red, _gradientStartColor.green, _gradientStartColor.blue, _gradientStartColor.alpha,
_gradientEndColor.red, _gradientEndColor.green, _gradientEndColor.blue, _gradientEndColor.alpha
};
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint gStartPoint = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint gEndPoint = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMaxY(currentBounds));
CGContextDrawLinearGradient(context, gradient, gStartPoint, gEndPoint, 0);
CGColorSpaceRelease(rgb);
CGGradientRelease(gradient);
}
Any ideas on how to do this in another way ?
Thanks !
In order to create a rounded corner view with a gradient background and drop shadow, here's what did:
The first part is very similar to what was provided in the question, it creates a rounded rect path using CGPathAddArcToPoint as described very well in this article. Here's a picture to help me understand it:
The second part works as follows:
Enable shadowing on the graphics context, add the path that was just defined, then fill that path. You can't apply the shadow to just the path itself (paths are not part of the graphics state), so you need to fill the path in order for the shadow to appear (I suppose a stroked path might also work?). You can't simply apply the shadow to a gradient since it's not really a standard fill (see this post for more info).
Once you have a filled rounded rect that creates the shadow, you need to draw the gradient over top of that. So add the path a second time in order to set the clipping area, then draw the gradient using CGContextDrawLinearGradient. I don't think you can easily "fill" a path with a gradient like you could with the earlier standard-fill step, so instead you fill the drawing area with the gradient and then clip to the rounded rectangle area that you're interested in.
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGGradientRef gradient = [self normalGradient];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGMutablePathRef outlinePath = CGPathCreateMutable();
float offset = 5.0;
float w = [self bounds].size.width;
float h = [self bounds].size.height;
CGPathMoveToPoint(outlinePath, nil, offset*2.0, offset);
CGPathAddArcToPoint(outlinePath, nil, offset, offset, offset, offset*2, offset);
CGPathAddLineToPoint(outlinePath, nil, offset, h - offset*2.0);
CGPathAddArcToPoint(outlinePath, nil, offset, h - offset, offset *2.0, h-offset, offset);
CGPathAddLineToPoint(outlinePath, nil, w - offset *2.0, h - offset);
CGPathAddArcToPoint(outlinePath, nil, w - offset, h - offset, w - offset, h - offset * 2.0, offset);
CGPathAddLineToPoint(outlinePath, nil, w - offset, offset*2.0);
CGPathAddArcToPoint(outlinePath, nil, w - offset , offset, w - offset*2.0, offset, offset);
CGPathCloseSubpath(outlinePath);
CGContextSetShadow(ctx, CGSizeMake(4,4), 3);
CGContextAddPath(ctx, outlinePath);
CGContextFillPath(ctx);
CGContextAddPath(ctx, outlinePath);
CGContextClip(ctx);
CGPoint start = CGPointMake(rect.origin.x, rect.origin.y);
CGPoint end = CGPointMake(rect.origin.x, rect.size.height);
CGContextDrawLinearGradient(ctx, gradient, start, end, 0);
CGPathRelease(outlinePath);
}
- (CGGradientRef)normalGradient
{
NSMutableArray *normalGradientLocations = [NSMutableArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
nil];
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:2];
UIColor *color = [UIColor colorWithRed:0.2745 green:0.2745 blue:0.2745 alpha:1.0];
[colors addObject:(id)[color CGColor]];
color = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1.0];
[colors addObject:(id)[color CGColor]];
NSMutableArray *normalGradientColors = colors;
int locCount = [normalGradientLocations count];
CGFloat locations[locCount];
for (int i = 0; i < [normalGradientLocations count]; i++)
{
NSNumber *location = [normalGradientLocations objectAtIndex:i];
locations[i] = [location floatValue];
}
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGGradientRef normalGradient = CGGradientCreateWithColors(space, (CFArrayRef)normalGradientColors, locations);
CGColorSpaceRelease(space);
return normalGradient;
}
I have solution that does not need pre-fill of the path. Advantage(?) is that the shadow can use transparency effects of the gradient (i.e. if gradient is from opaque to trasparent, shadow will be partially transparent as well) and is simpler.
It goes more or less like:
CGContextSetShadowWithColor();
CGContextBeginTransparencyLayer();
CGContextSaveGState();
CGContextClip();
CGGradientCreateWithColorComponents();
CGContextRestoreGState();
CGContextEndTransparencyLayer();
CGContextSetShadowWithColor(..., NULL);
I suppose that is beacuse CGContextBeginTransparencyLayer/CGContextEndTransparencyLayer is outside the clip and the shadow is applied to that layer (which contains gradient filled path). At least it seems to work for me.
For shadows you can use CGContextSetShadow()
This code will draw something with a shadow:
- (void)drawTheRealThingInContext:(CGContextRef)ctx
{
// calculate x, y, w, h and inset here...
CGContextMoveToPoint(ctx, x+inset, y);
CGContextAddLineToPoint(ctx, x+w-inset, y);
CGContextAddArcToPoint(ctx, x+w, y, x+w, y+inset, inset);
CGContextAddLineToPoint(ctx, x+w, y+w-inset);
CGContextAddArcToPoint(ctx,x+w, y+w, x+w-inset, y+w, inset);
CGContextAddLineToPoint(ctx, x+inset, y+w);
CGContextAddArcToPoint(ctx,x, y+w, x, y+w-inset, inset);
CGContextAddLineToPoint(ctx, x, y+inset);
CGContextAddArcToPoint(ctx,x, y, x+inset, y, inset);
}
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat color[4];color[0] = 1.0;color[1] = 1.0;color[2] = 1.0;color[3] = 1.0;
CGFloat scolor[4];scolor[0] = 0.4;scolor[1] = 0.4;scolor[2] = 0.4;scolor[3] = 0.8;
CGContextSetFillColor(ctx, color);
CGContextSaveGState(ctx);
CGSize myShadowOffset = CGSizeMake (3, -3);
CGContextSetShadow (ctx, myShadowOffset, 1);
CGContextBeginPath(ctx);
[self drawTheRealThingInContext:ctx];
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
}
Your (original) problem was that you were again drawing a shadow when you drew the gradient. This shadow had a (0,0) offset and a little bit of blur, that only shines through on the corners. In the line before CGContextDrawLinearGradient(…), add the following:
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 0, NULL);
The NULL color value disables shadowing and will remove the corner effect.

UIImage rounded corners

I try to get rounded corners on a UIImage, what I read so far, the easiest way is to use a mask images. For this I used code from TheElements iPhone Example and some image resize code I found. My problem is that resizedImage is always nil and I don't find the error...
- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize
{
CGSize imageSize = [self size];
float width = imageSize.width;
float height = imageSize.height;
// scaleFactor will be the fraction that we'll
// use to adjust the size. For example, if we shrink
// an image by half, scaleFactor will be 0.5. the
// scaledWidth and scaledHeight will be the original,
// multiplied by the scaleFactor.
//
// IMPORTANT: the "targetHeight" is the size of the space
// we're drawing into. The "scaledHeight" is the height that
// the image actually is drawn at, once we take into
// account the ideal of maintaining proportions
float scaleFactor = 0.0;
float scaledWidth = targetSize.width;
float scaledHeight = targetSize.height;
CGPoint thumbnailPoint = CGPointMake(0,0);
// since not all images are square, we want to scale
// proportionately. To do this, we find the longest
// edge and use that as a guide.
if ( CGSizeEqualToSize(imageSize, targetSize) == NO )
{
// use the longeset edge as a guide. if the
// image is wider than tall, we'll figure out
// the scale factor by dividing it by the
// intended width. Otherwise, we'll use the
// height.
float widthFactor = targetSize.width / width;
float heightFactor = targetSize.height / height;
if ( widthFactor < heightFactor )
scaleFactor = widthFactor;
else
scaleFactor = heightFactor;
// ex: 500 * 0.5 = 250 (newWidth)
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the thumbnail in the frame. if
// wider than tall, we need to adjust the
// vertical drawing point (y axis)
if ( widthFactor < heightFactor )
thumbnailPoint.y = (targetSize.height - scaledHeight) * 0.5;
else if ( widthFactor > heightFactor )
thumbnailPoint.x = (targetSize.width - scaledWidth) * 0.5;
}
CGContextRef mainViewContentContext;
CGColorSpaceRef colorSpace;
colorSpace = CGColorSpaceCreateDeviceRGB();
// create a bitmap graphics context the size of the image
mainViewContentContext = CGBitmapContextCreate (NULL, targetSize.width, targetSize.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
// free the rgb colorspace
CGColorSpaceRelease(colorSpace);
if (mainViewContentContext==NULL)
return NULL;
//CGContextSetFillColorWithColor(mainViewContentContext, [[UIColor whiteColor] CGColor]);
//CGContextFillRect(mainViewContentContext, CGRectMake(0, 0, targetSize.width, targetSize.height));
CGContextDrawImage(mainViewContentContext, CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledWidth, scaledHeight), self.CGImage);
// Create CGImageRef of the main view bitmap content, and then
// release that bitmap context
CGImageRef mainViewContentBitmapContext = CGBitmapContextCreateImage(mainViewContentContext);
CGContextRelease(mainViewContentContext);
CGImageRef maskImage = [[UIImage imageNamed:#"Mask.png"] CGImage];
CGImageRef resizedImage = CGImageCreateWithMask(mainViewContentBitmapContext, maskImage);
CGImageRelease(mainViewContentBitmapContext);
// convert the finished resized image to a UIImage
UIImage *theImage = [UIImage imageWithCGImage:resizedImage];
// image is retained by the property setting above, so we can
// release the original
CGImageRelease(resizedImage);
// return the image
return theImage;
}
If you are using a UIImageView to display the image you can simply do the following:
imageView.layer.cornerRadius = 5.0;
imageView.layer.masksToBounds = YES;
And to add a border:
imageView.layer.borderColor = [UIColor lightGrayColor].CGColor;
imageView.layer.borderWidth = 1.0;
I believe that you'll have to import <QuartzCore/QuartzCore.h> and link against it for the above code to work.
How about these lines...
// Get your image somehow
UIImage *image = [UIImage imageNamed:#"image.jpg"];
// Begin a new image that will be the new image with the rounded corners
// (here with the size of an UIImageView)
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
// Add a clip before drawing anything, in the shape of an rounded rect
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:10.0] addClip];
// Draw your image
[image drawInRect:imageView.bounds];
// Get the image, here setting the UIImageView image
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
// Lets forget about that we were drawing
UIGraphicsEndImageContext();
I created an UIImage-extension in swift, based on #epatel's great answer:
extension UIImage{
var roundedImage: UIImage {
let rect = CGRect(origin:CGPoint(x: 0, y: 0), size: self.size)
UIGraphicsBeginImageContextWithOptions(self.size, false, 1)
defer {
// End context after returning to avoid memory leak
UIGraphicsEndImageContext()
}
UIBezierPath(
roundedRect: rect,
cornerRadius: self.size.height
).addClip()
self.drawInRect(rect)
return UIGraphicsGetImageFromCurrentImageContext()
}
}
Tested in a storyboard:
The problem was the use of CGImageCreateWithMask which returned an all black image. The solution I found was to use CGContextClipToMask instead:
CGContextRef mainViewContentContext;
CGColorSpaceRef colorSpace;
colorSpace = CGColorSpaceCreateDeviceRGB();
// create a bitmap graphics context the size of the image
mainViewContentContext = CGBitmapContextCreate (NULL, targetSize.width, targetSize.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
// free the rgb colorspace
CGColorSpaceRelease(colorSpace);
if (mainViewContentContext==NULL)
return NULL;
CGImageRef maskImage = [[UIImage imageNamed:#"mask.png"] CGImage];
CGContextClipToMask(mainViewContentContext, CGRectMake(0, 0, targetSize.width, targetSize.height), maskImage);
CGContextDrawImage(mainViewContentContext, CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledWidth, scaledHeight), self.CGImage);
// Create CGImageRef of the main view bitmap content, and then
// release that bitmap context
CGImageRef mainViewContentBitmapContext = CGBitmapContextCreateImage(mainViewContentContext);
CGContextRelease(mainViewContentContext);
// convert the finished resized image to a UIImage
UIImage *theImage = [UIImage imageWithCGImage:mainViewContentBitmapContext];
// image is retained by the property setting above, so we can
// release the original
CGImageRelease(mainViewContentBitmapContext);
// return the image
return theImage;
Extending Besi's excellent answer, with correct scale, in Swift 4:
extension UIImage {
public func rounded(radius: CGFloat) -> UIImage {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
draw(in: rect)
return UIGraphicsGetImageFromCurrentImageContext()!
}
}
You aren't actually doing anything other than scaling there. What you need to do is to "mask" the corners of the image by clipping it with a CGPath. For instance -
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginTransparencyLayerWithRect(context, self.frame, NULL);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
CGFloat roundRadius = (radius) ? radius : 12.0;
CGFloat minx = CGRectGetMinX(self.frame), midx = CGRectGetMidX(self.frame), maxx = CGRectGetMaxX(self.frame);
CGFloat miny = CGRectGetMinY(self.frame), midy = CGRectGetMidY(self.frame), maxy = CGRectGetMaxY(self.frame);
// draw the arcs, handle paths
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, roundRadius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, roundRadius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, roundRadius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, roundRadius);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFill);
CGContextEndTransparencyLayer(context);
}
I suggest checking out the Quartz 2D programming guide or some other samples.
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 *)imageWithRoundCorner:(UIImage*)img andCornerSize:(CGSize)size
{
UIImage * newImage = nil;
if( nil != img)
{
#autoreleasepool {
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, size.width, size.height);
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);
}
}
return newImage;
}
I think this could be very related:
In iOS 11 there is a very elgant way of rounding each single corner of a (Image)View.
let imageView = UIImageView(image: UIImage(named: "myImage"))
imageView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
imageView.layer.cornerRadius = 10.0
I liked the answer of #samwize, however it caused me nasty memory leaks when used with collectionView.
To fix it I found that UIGraphicsEndImageContext() was missing
extension UIImage {
/**
Rounds corners of UIImage
- Parameter proportion: Proportion to minimum paramter (width or height)
in order to have the same look of corner radius independetly
from aspect ratio and actual size
*/
func roundCorners(proportion: CGFloat) -> UIImage {
let minValue = min(self.size.width, self.size.height)
let radius = minValue/proportion
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: self.size)
UIGraphicsBeginImageContextWithOptions(self.size, false, 1)
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
self.draw(in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext() ?? self
UIGraphicsEndImageContext()
return image
}
}
Feel free to just pass the radius instead of proportion. proportion is used because I have collectionView scroll and images have different sizes, therefore when using constant radius it actually looks different in terms of proprtions (example: two images, one is 1000x1000 and another 2000x2000, corner radius of 30 will look different on each one of them)
So if you do image.roundCorners(proportion: 20) all the pictures look like the have the same corner radius.
This answer is also an updated version.
The reason it worked with clipping, not with masking, seems to be the color space.
Apple Documentation's below.
mask
A mask. If the mask is an image, it must be in the DeviceGray color space, must not have an alpha component, and may not itself be masked by an image mask or a masking color. If the mask is not the same size as the image specified by the image parameter, then Quartz scales the mask to fit the image.
Hi guys try this code,
+ (UIImage *)roundedRectImageFromImage:(UIImage *)image withRadious:(CGFloat)radious {
if(radious == 0.0f)
return image;
if( image != nil) {
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
CGRect rect = CGRectMake(0.0f, 0.0f, imageWidth, imageHeight);
UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
const CGFloat scale = window.screen.scale;
UIGraphicsBeginImageContextWithOptions(rect.size, NO, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
CGContextSaveGState(context);
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM (context, radious, radious);
CGFloat rectWidth = CGRectGetWidth (rect)/radious;
CGFloat rectHeight = CGRectGetHeight (rect)/radious;
CGContextMoveToPoint(context, rectWidth, rectHeight/2.0f);
CGContextAddArcToPoint(context, rectWidth, rectHeight, rectWidth/2.0f, rectHeight, radious);
CGContextAddArcToPoint(context, 0.0f, rectHeight, 0.0f, rectHeight/2.0f, radious);
CGContextAddArcToPoint(context, 0.0f, 0.0f, rectWidth/2.0f, 0.0f, radious);
CGContextAddArcToPoint(context, rectWidth, 0.0f, rectWidth, rectHeight/2.0f, radious);
CGContextRestoreGState(context);
CGContextClosePath(context);
CGContextClip(context);
[image drawInRect:CGRectMake(0.0f, 0.0f, imageWidth, imageHeight)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
return nil;
}
Cheers !!!
It's very easy to create a rounded image when you make use of the image dimension.
cell.messageImage.layer.cornerRadius = image.size.width / 2
cell.messageImage.layer.masksToBounds = true
Found out the best and simple way of doing it is as follows (no answer did that):
UIImageView *imageView;
imageView.layer.cornerRadius = imageView.frame.size.width/2.0f;
imageView.layer.masksToBounds = TRUE;
Pretty simple and done this right.
See here...
IMO unless you absolutely need to do it in code, just overlay an image on top.
Something along the lines of...
- (void)drawRect:(CGRect)rect
{
// Drawing code
[backgroundImage drawInRect:rect];
[buttonOverlay drawInRect:rect];
}
For Creating a Round Corner image we can use quartzcore.
First How to add QuartzCore framework?
Click project -Targets
->project
->BuildPhase
->Link Binary with Libraries
->Then click + symbol finally select from list and add it
or else
Click project -Targets
->Targets
->general
->Linked Frameworks and Libraries
->Then click + symbol finally select from list and add the QuartzCore framework
Now import
#import <QuartzCore/QuartzCore.h>
in your ViewController
Then in viewDidLoad method
self.yourImageView.layer.cornerRadius = 5.0;
self.yourImageView.layer.borderWidth = 1.0f;
self.yourImageView.layer.borderColor = [UIColor blackColor].CGColor;
self.yourImageView.layer.masksToBounds = YES;
I was struggling to round the corners of a UIImage box in my storyboard. I had a IBOutlet for my UIImage called image. After reading a bunch of posts on here, I simply added 3 lines and that worked perfectly.
import UIKit
Then in viewDidLoad:
image.layer.cornerRadius = 20.0
image.layer.masksToBounds = true
This is for iOS 11.1 in Xcode 9.