I want make UI like the below image. Now I want to make it by QuartzCore.
I had implemented all the Graphics including circles,Vertical and horizontal lines.
I am little confuse how to make that center point with animation effect shown in Image.
The key to the approach used to draw the image shown above is using CGContextDrawRadialGradient to draw the dot. We can then use this dot image as the contents of a CALayer, allowing us to reposition it and animate it as we see fit.
In your original question you asked...
how to make that center point with animation effect shown in Image?
...but as a still image it doesn't show any animation. I've made an educated guess as to how you envisioned the dot be animated.
The following code creates a gradated dot with a pulsating animation, fading in and out repeatedly:
Code
#import <QuartzCore/QuartzCore.h>
...
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat dotDiameter = 30.f;
CGFloat dotRadius = dotDiameter * 0.5;
CGRect dotRect = CGRectMake(CGRectGetMidX(self.view.frame) - dotRadius,
CGRectGetMidY(self.view.frame) - dotRadius,
dotDiameter, dotDiameter);
CALayer *dotLayer = [CALayer layer];
dotLayer.contents = (id)[self dotImageOfDiameter:dotDiameter].CGImage;
dotLayer.cornerRadius = dotRadius;
dotLayer.frame = dotRect;
dotLayer.masksToBounds = YES;
// Animate the dot to make it appear to pulsate.
CABasicAnimation *pulseAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
pulseAnimation.autoreverses = YES;
pulseAnimation.duration = 0.8;
pulseAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pulseAnimation.repeatCount = INFINITY;
pulseAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[dotLayer addAnimation:pulseAnimation forKey:#"opacity"];
[self.view.layer addSublayer:dotLayer];
}
- (UIImage *)dotImageOfDiameter:(CGFloat)diameter
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = diameter * 0.5;
CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colours[8] = { 0.56f, 0.78f, 0.94f, 1.0f, // Opaque dot colour.
0.56f, 0.78f, 0.94f, 0.0f }; // Transparent dot colour.
CGGradientRef gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, NULL, 2);
CGContextDrawRadialGradient(context, gradient, CGPointMake(radius, radius), 0.0f, CGPointMake(radius, radius), radius, kCGGradientDrawsAfterEndLocation);
CGImageRef dotImageRef = CGBitmapContextCreateImage(context);
UIImage *dotImage = [UIImage imageWithCGImage:dotImageRef];
CGColorSpaceRelease(baseColorSpace);
CGGradientRelease(gradient);
CGImageRelease(dotImageRef);
UIGraphicsEndImageContext();
return dotImage;
}
This one is from apple, shows how to add shadows in Quarrtz. Here it is explained
For shadow you can use the right color values nearly equal to cyan color in ur case,
void MyDrawWithShadows (CGContextRef myContext, // 1
CGFloat wd, CGFloat ht);
{
CGSize myShadowOffset = CGSizeMake (-15, 20);// 2
CGFloat myColorValues[] = {1, 0, 0, .6};// 3
CGColorRef myColor;// 4
CGColorSpaceRef myColorSpace;// 5
CGContextSaveGState(myContext);// 6
CGContextSetShadow (myContext, myShadowOffset, 5); // 7
// Your drawing code here// 8
CGContextSetRGBFillColor (myContext, 0, 1, 0, 1);
CGContextFillRect (myContext, CGRectMake (wd/3 + 75, ht/2 , wd/4, ht/4));
myColorSpace = CGColorSpaceCreateDeviceRGB ();// 9
myColor = CGColorCreate (myColorSpace, myColorValues);// 10
CGContextSetShadowWithColor (myContext, myShadowOffset, 5, myColor);// 11
// Your drawing code here// 12
CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
CGContextFillRect (myContext, CGRectMake (wd/3-75,ht/2-100,wd/4,ht/4));
CGColorRelease (myColor);// 13
CGColorSpaceRelease (myColorSpace); // 14
CGContextRestoreGState(myContext);// 15
}
You can draw radial gradient to some CALayer and then just animate that CALayer
Use CGContextDrawRadialGradient to draw gradient layer
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
[anim setFromValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]];
// Scale x and y up
[anim setToValue:[NSValue valueWithCATransform3D:
CATransform3DMakeScale (1.0f, 1.0f,0.0f)]];
[anim setAutoreverses:YES];
[anim setRepeatCount:HUGE_VALF];
[gradientViewlayer addAnimation:anim];
Hope this helps, I havent tested this, but may be this can give you some directions.
Other layout you can complete with CGContext
Related
I got a requirements to display a UILabel with background split between two colors, like in this image:
(colors here are black at the bottom and 50% gray at the top - but this is not important). I tried setting the label's background colour to 50% grey in the interface builder and then do this in the code:
CALayer *sl1 = [[[CALayer alloc] init] autorelease];
sl1.frame = CGRectMake(0, lbl.frame.size.height / 2, lbl.frame.size.width, score1.frame.size.height/2);
sl1.backgroundColor = [[UIColor blackColor] CGColor];
[lbl.layer insertSublayer:sl1 atIndex:0];
Unfortunately, this resulted in the black part being drawn over the text, so the label looks like this:
which is, needless to say, is not something I need. So how can I get this background without turning to custom images? The issue is I need to have UILabel's like this in several places, different sizes - so I would need to create multiple versions of the background image.
Any ideas? Thanks.
this works:
UILabel* myLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
myLabel.text = #"Ciao";
myLabel.textColor = [UIColor greenColor];
UIGraphicsBeginImageContext(CGSizeMake(100, 50));
CGContextRef context = UIGraphicsGetCurrentContext();
// drawing with a gray fill color
CGContextSetRGBFillColor(context, 0.4, 0.4, 0.4, 1.0);
// Add Filled Rectangle,
CGContextFillRect(context, CGRectMake(0.0, 0.0, 100, 50));
// drawing with a black fill color
CGContextSetRGBFillColor(context, 0., 0., 0., .9);
// Add Filled Rectangle,
CGContextFillRect(context, CGRectMake(0.0, 25, 100, 25));
UIImage* resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
myLabel.backgroundColor = [UIColor colorWithPatternImage:resultingImage];
[self.view addSubview:myLabel];
Use UIColor's +colorWithPatternImage:. Pass in a 1px by the UILabel's height image and it will be "tiled" across the the width of the view.
myLabel.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"label-background.png"]];
first you have to subclass the UILabel and override it's drawRect: method like this for gradient background
- (void)drawRect:(CGRect)rect
{
//////////////GET REFERENCE TO CURRENT GRAPHICS CONTEXT
CGContextRef context = UIGraphicsGetCurrentContext();
//////////////CREATE BASE SHAPE WITH ROUNDED CORNERS FROM BOUNDS
CGRect activeBounds = self.bounds;
CGFloat cornerRadius = 10.0f;
CGFloat inset = 6.5f;
CGFloat originX = activeBounds.origin.x + inset;
CGFloat originY = activeBounds.origin.y + inset;
CGFloat width = activeBounds.size.width - (inset*2.0f);
CGFloat height = activeBounds.size.height - (inset*2.0f);
CGRect bPathFrame = CGRectMake(originX, originY, width, height);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:bPathFrame cornerRadius:cornerRadius].CGPath;
//////////////CREATE BASE SHAPE WITH FILL AND SHADOW
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 6.0f, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:1.0f].CGColor);
CGContextDrawPath(context, kCGPathFill);
//////////////CLIP STATE
CGContextSaveGState(context); //Save Context State Before Clipping To "path"
CGContextAddPath(context, path);
CGContextClip(context);
//////////////DRAW GRADIENT
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
size_t count = 3;
CGFloat locations[3] = {0.0f, 0.57f, 1.0f};
CGFloat components[12] =
{ 0.0f/255.0f, 0.0f/255.0f, 0.0f/255.0f, 1.0f, //1
5.0f/255.0f, 5.0f/255.0f, 5.0f/255.0f, 1.0f, //2
10.0f/255.0f, 10.0f/255.0f, 10.0f/255.0f, 1.0f}; //3
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, count);
CGPoint startPoint = CGPointMake(activeBounds.size.width * 0.5f, 0.0f);
CGPoint endPoint = CGPointMake(activeBounds.size.width * 0.5f, activeBounds.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
This will draw black to white gradient background
May this will help you
Happy Codding :)
the above code is from this site http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-uialertview-custom-graphics/
I want to make an effect like what you see on the right side of the first cell here:
I'm guessing it's some sort of overlay view with a gradient, but I just can't figure out how it's configured with transparency. I tried making my own overlay view with a gradient and set the alpha of the colors down, but it just shows up as a gray - white gradient.
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *gradBegin = [UIColor colorWithWhite:1.0 alpha:0.8];
UIColor *gradEnd = [UIColor colorWithWhite:1.0 alpha:0.6];
NSArray* gradientColors = [NSArray arrayWithObjects:
(id)gradBegin.CGColor,
(id)gradEnd.CGColor, nil];
CGFloat gradientLocations[] = {0, 1};
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations);
CGContextDrawLinearGradient(context, gradient, CGPointMake(rect.origin.x, rect.origin.y + rect.size.height/2.0),
CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height/2.0), 0);
What exactly is going on in this screenshot, and how can I replicate it?
I wrote a simple UIView class that will draw itself faded. It is a basic UIView with the drawRect overrided:
- (void)drawRect:(CGRect)rect
{
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor* gradBegin = [UIColor colorWithWhite:1 alpha:0];
UIColor* gradEnd = [UIColor colorWithWhite:1 alpha:1];
NSArray* gradColours = [NSArray arrayWithObjects:
(id)gradBegin.CGColor,
(id)gradBegin.CGColor,
(id)gradEnd.CGColor,
(id)gradEnd.CGColor, nil];
CGFloat gradLocs[] = { 0, 0.5, 0.9, 1 };
CGGradientRef gradient = CGGradientCreateWithColors(colourSpace, (__bridge CFArrayRef)gradColours, gradLocs);
CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(self.frame.size.width, 0), 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colourSpace);
}
This works if overlayed over your view and the view's background is white.
While this certainly can be done with Core Graphics, it's infinitely easier to add a transparent PNG to your project with the appropriate width and 1 pixel height and then position a UIImageView in your table cell over the content to create this effect.
The gradient drawing code in your question looks ok, although I haven't tested it. I suggest setting gradBegin.alpha = 0 and gradEnd.alpha = 1. Also, the last line could be simplified:
CGContextDrawLinearGradient(context, gradient, rect.origin,
CGPointMake(CGRectGetMaxX(rect), rect.origin.y, 0);
And don't forget to CGGradientRelease(gradient).
Anyway, other than that, you need to set overlayView.opaque = NO.
I have to rotate a UIImage before I save it out to file. I've tried a number of methods, some of which seem to work for other people, but all it seems to do it turn my image solid white. Here's my code:
CGFloat radians = 90.0f * M_PI/180;
UIGraphicsBeginImageContext(image.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx, CGAffineTransformMakeRotation(radians));
CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Can anyone spot what I'm doing wrong? The variable "image" is a UIImage.
- (UIImage *)imageRotatedByRadians:(CGFloat)radians
{
// calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(radians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
//Rotate the image context
CGContextRotateCTM(bitmap, radians);
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
I believe you are not taking into account that the coordinate system for iPhone and Quartz Coordinate system are different.
try putting these two lines before CGContextConcatCTM.
// to account for Quartz coordinate system.
CGContextScaleCTM(cx, 1, -1);
CGContextTranslateCTM(ctx, 0, -longerSide); // width or height depending on the orientation
I suggest to put your image in UIImageView and then use following code:
- (void)setupRotatingButtons
{
// call this method once; make sure "self.view" is not nil or the button
// won't appear. the below variables are needed in the #interface.
// center: the center of rotation
// radius: the radius
// time: a CGFloat that determines where in the cycle the button is located at
// (note: it will keep increasing indefinitely; you need to use
// modulus to find a meaningful value for the current position, if
// needed)
// speed: the speed of the rotation, where 2 * 3.1415 is **roughly** 1 lap a
// second
center = CGPointMake(240, 160);
radius = 110;
time = 0;
speed = .3 * 3.1415; // <-- will rotate CW 360 degrees per .3 SECOND (1 "lap"/s)
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:self selector:#selector(continueCircling:)];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)continueCircling:(CADisplayLink *)dl
{
time += speed * dl.duration;
//here rotate your image view
yourImageView.transform = CGAffineTransformMakeRotation(time);
}
Use following code to rotate:
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"image1.png"]];
[imageView setFrame:CGRectMake(0.0f, 0.0f,100.0f, 100.0f)];
[self.view addSubview:imageView];
self.imageView.transform = CGAffineTransformMakeRotation((90.0f * M_PI) / 180.0f);
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.
I am trying to add a drop shadow to a UIImage view. I get a shadow but it is clipped to the edges of the image view and I am not sure why since I correctly set the uiimageview.clipsToBounds to NO. Below is the code:
-(void)addShadow
{
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef myContext = UIGraphicsGetCurrentContext();
float myColorValues[] = {0, 0, 0, darkness};// 3
CGColorRef myColor;// 4
CGColorSpaceRef myColorSpace;
CGContextSaveGState(myContext);// 6
myColorSpace = CGColorSpaceCreateDeviceRGB ();// 9
myColor = CGColorCreate (myColorSpace, myColorValues);// 10
CGContextSetShadowWithColor (myContext, myShadowOffset, spread, myColor);// 11
// Your drawing code here// 12
// CGContextDrawImage(myContext, rotatingView.frame,imgRef);
rotatingView.clipsToBounds = NO;
[rotatingView.image drawInRect:rotatingView.frame
blendMode:kCGBlendModeNormal alpha:.5];
CGColorRelease (myColor);// 13
CGColorSpaceRelease (myColorSpace); // 14
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(myContext);
UIGraphicsEndImageContext();
rotatingView.image = imageCopy;
}
I believe the CGContextRef you're passed also has clipping set, to prevent basically this exact behavior. You might want to try just adding a CALayer:
CALayer *layer = [CALayer layer];
CGRect bounds = self.bounds;
layer.bounds = bounds;
layer.position = CGPointMake(bounds.size.width / 2 + 5, bounds.size.height / 2 + 5);
layer.backgroundColor = [UIColor colorWithWhite: 0.10 alpha: 0.75].CGColor;
layer.zPosition = -5;
[self.layer addSublayer: layer];