drawRect Circle inside of circle used to show progress - iphone

I'm trying to show progress by having a circle fill up with another circle inside of it. Can someone help me achieve this? I may be going about this the wrong way.
Here is my code:
- (void)drawRect:(CGRect)rect
{
rect = CGRectMake(0, 400, 500, 500);
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 500, 500) cornerRadius:50.0] addClip];
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, 2.0);
CGContextSetRGBFillColor(contextRef, 111/255.0f, 116/255.0f, 128/255.0f, 1.0);
CGRect circlePoint = (CGRectMake(0, 0, rect.size.width, rect.size.height));
CGContextFillEllipseInRect(contextRef, circlePoint);
CGContextBeginPath(contextRef);
float c = 20; //Number that should cause the green progress to increase and decrease
CGContextAddArc(contextRef, 250, 250, 250, 0, M_PI, 0);
CGContextClosePath(contextRef); // could be omitted
UIColor *color = [UIColor greenColor];
CGContextSetFillColorWithColor(contextRef, color.CGColor);
CGContextFillPath(contextRef);
}
And here is a screenshot of how it looks right now. I basically want to be able to control the progress of the green inside using my float c; variable.
I've tried playing around with CGContextAddArc, but I can't get grow to the size of the circle as "c" increases and same when I decrease the value of "c"

use your c to say from where to where
// draw the pie part
CGContextMoveToPoint(_viewContext, centerX, centerY);
CGContextAddArc(_viewContext, centerX, centerY, pieRadius, radians(startDegrees), radians(endDegrees), 0);
CGContextClosePath(_viewContext);
CGContextFillPath(_viewContext);
startDegrees = 0
endDegress = c
_viewContext = contextRef
centerX/CenterY = center of bounds
PieRadius = radius you want :D in pixels IIRC
to convert deg to rad
#define pi 3.14159265
double radians(double percentage)
{
return (percentage/100)*360 * (pi/180);
}

Related

iOS - scaling text vertically on Quartz

I am writing some text to a quartz context.
I have a box of 500 x 200 pixels and I am writing some text inside. The box can have any aspect ratio but the text will always write with the font proportions as it is. What I want is to scale the font vertically and keep the horizontal scale.
See the picture. The first box represents what I am getting, the second one, what I want.
this is how I am writing it...
CGRect rect = CGRectMake(0,0,500,200);
[myText drawInRect:rect
withFont:aFont
lineBreakMode:UILineBreakModeTailTruncation
alignment:UITextAlignmentCenter];
is there a way to scale the font vertically?
NOTE: as this is being written in a PDF context, I don't want to
render the text to a image context and scale it vertically, because I
don't want to lose resolution.
thanks
- (void) scaleTextVerticallyWithContext:(CGContextRef)myContext withRect:(CGRect)contextRect
{
CGFloat w, h;
w = contextRect.size.width;
h = contextRect.size.height;
CGAffineTransform myTextTransform;
CGContextSelectFont (myContext, "Helvetica-Bold", h/10, kCGEncodingMacRoman);
CGContextSetCharacterSpacing (myContext, 10);
CGContextSetTextDrawingMode (myContext, kCGTextFillStroke);
CGContextSetRGBFillColor (myContext, 0, 1, 0, .5);
CGContextSetRGBStrokeColor (myContext, 0, 0, 1, 1);
/*
* CGAffineTransformMakeScale ( CGFloat sx, CGFloat sy );
* sx: The factor by which to scale the x-axis of the coordinate system.
* sy: The factor by which to scale the y-axis of the coordinate system.
*/
myTextTransform = CGAffineTransformMakeScale(1,8);
CGContextSetTextMatrix (myContext, myTextTransform);
CGContextShowTextAtPoint (myContext, 40, 0, "Hello There", 9);
}
- (void)drawRect:(CGRect)rect{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect viewBounds = self.bounds;
CGContextTranslateCTM(context, 0, viewBounds.size.height);
CGContextScaleCTM(context, 1, -1);
[self scaleTextVerticallyWithContext:context withRect:viewBounds];
}

Two-color background for UILabel

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/

iPhone how to clip half of the ellipse

I have drawn ellipse:
CGContextFillEllipseInRect(contextRef, CGRectMake(50, 50, 50, 128));
But i only need a half of ellipse, is there a way to clip the other half?
Before calling the drawing method you can clip the context to a portion of the ellipse:
CGContextSaveGState(contextRef);
BOOL onlyDrawTopHalf = YES;
CGFloat halfMultiplier = onlyDrawTopHalf ? -1.0 : 1.0;
CGRect ellipse = CGRectMake(50, 50, 50, 128);
CGRect clipRect = CGRectOffset(ellipse, 0, halfMultiplier * ellipse.size.height / 2);
CGContextClipToRect(contextRef, clipRect);
CGContextFillEllipseInRect(contextRef, ellipse);
// restore the context: removes the clipping
CGContextRestoreGState(contextRef);

iPhone: Create CGLayer with gradient point and transparent background

I would like to create a CGLayer, contains a point with gradient in boundary. Because I will use this layer to draw multiple points on the main screen.
This is my code:
CGRect r = CGRectMake(0, 0, 64, 64);
textureLayer = CGLayerCreateWithContext(context, r.size, NULL);
CGContextRef textureContext = CGLayerGetContext(textureLayer);
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat colors[] =
{
0.0, 0.0, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0
};
gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
CGColorSpaceRelease(rgb);
CGPoint start, end;
start = end = CGPointMake(32, 32);
CGFloat startRadius = 20;
CGFloat endRadius = 30;
CGContextSaveGState(textureContext);
CGGradientDrawingOptions options = 0 | kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(textureContext, gradient, start, startRadius, end, endRadius, options);
CGContextRestoreGState(textureContext);
I got this result:
It is correct. But when I draw on main screen, not the stroke like I expect, this is the result:
I think the problem is the white background in my CGLayer. How I can make that background be transparent?
Thank for your help.

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.