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 locationslocCount; for (int I = 0; I.
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 locationslocCount; for (int I = 0; I.
Thank you beno for this very detailed explanation! Your example works perfectly. I would just correct your CGGradientRef declaration from CGGradientRef *normalGradient to CGGradientRef normalGradient.
– super_tomtom Nov 23 '10 at 15:07 Can you explain how the tangent lines and points are created from the arguments? I don't get how you can create two lines and two points from 4 arguments... – ryyst Apr 16 at 12:54 @super_tomtom can someone explain how this works? I've subclasses a custom UIView and made this function it's drawRect override but I still get no rounded corners or drop shadow?
– Straddle Apr 19 at 20:31.
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 color4;color0 = 1.0;color1 = 1.0;color2 = 1.0;color3 = 1.0; CGFloat scolor4;scolor0 = 0.4;scolor1 = 0.4;scolor2 = 0.4;scolor3 = 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); }.
Yes, this surely works, but my problem is that I want to fill the shape with a gradient. To do so, I need to use the CGContextClip function. And once the context is clipped, it doesn't seems to draw the shadow anymore.
– super_tomtom Jul 2 '10 at 7:46.
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.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.