How can I draw a sector (filled arc) with radial gradient in objective c (Core Graphics)
I use
CGContextDrawRadialGradient
but it draws circle.
It will be good if you say me how to fill any shape by radial gradient. Thanks
Add a path to current context that defines the desired shape and clip the context before drawing. Sample code:
- (void)drawRect:(CGRect)rect
{
CGPoint c = self.center ;
// Drawing code
CGContextRef cx = UIGraphicsGetCurrentContext();
CGContextSaveGState(cx);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGFloat comps[] = {1.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0};
CGFloat locs[] = {0,1};
CGGradientRef g = CGGradientCreateWithColorComponents(space, comps, locs, 2);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, c.x, c.y);
CGPathAddLineToPoint(path, NULL, c.x, c.y-100);
CGPathAddArcToPoint(path, NULL, c.x+100, c.y-100, c.x+100, c.y, 100);
CGPathAddLineToPoint(path, NULL, c.x, c.y);
CGContextAddPath(cx, path);
CGContextClip(cx);
CGContextDrawRadialGradient(cx, g, c, 1.0f, c, 320.0f, 0);
CGContextRestoreGState(cx);
... // Do some more drawing may be
}
Related
I'm trying to get into Core Graphics.
A the Moment I'm trying to draw a certain shape out of 2 rects. Now I want to join those booth to work with gradient and color functions. I'm not sure if there even is a way.
Here is a Picture:
Here are my Code Snippets:
-(void)drawRoundedRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect frame1 = CGRectMake(self.bounds.origin.x, self.bounds.origin.y+45, self.bounds.size.width, self.bounds.size.height-45);
CGRect frame2 = CGRectMake(self.bounds.size.width-80, self.bounds.origin.y+25, 80, 50);
CGPathRef roundedRectPath1 = [self newPathForRoundedRect:frame1 radius:15];
CGPathRef roundedRectPath2 = [self newPathForRoundedRect:frame2 radius:11];
CGContextSetRGBFillColor(ctx, 200, 200, 200, 0.5);
CGContextAddPath(ctx, roundedRectPath1);
CGContextFillPath(ctx);
CGPathRelease(roundedRectPath1);
CGContextAddPath(ctx, roundedRectPath2);
CGContextFillPath(ctx);
CGPathRelease(roundedRectPath2);
}
-(CGPathRef) newPathForRoundedRect:(CGRect)rect radius:(CGFloat)radius
{
CGMutablePathRef retPath = CGPathCreateMutable();
CGRect innerRect = CGRectInset(rect, radius, radius);
CGFloat inside_right = innerRect.origin.x + innerRect.size.width;
CGFloat outside_right = rect.origin.x + rect.size.width;
CGFloat inside_bottom = innerRect.origin.y + innerRect.size.height;
CGFloat outside_bottom = rect.origin.y + rect.size.height;
CGFloat inside_top = innerRect.origin.y;
CGFloat outside_top = rect.origin.y;
CGFloat outside_left = rect.origin.x;
CGPathMoveToPoint(retPath, NULL, innerRect.origin.x, outside_top);
CGPathAddLineToPoint(retPath, NULL, inside_right, outside_top);
CGPathAddArcToPoint(retPath, NULL, outside_right, outside_top, outside_right, inside_top, radius);
CGPathAddLineToPoint(retPath, NULL, outside_right, inside_bottom);
CGPathAddArcToPoint(retPath, NULL, outside_right, outside_bottom, inside_right, outside_bottom, radius);
CGPathAddLineToPoint(retPath, NULL, innerRect.origin.x, outside_bottom);
CGPathAddArcToPoint(retPath, NULL, outside_left, outside_bottom, outside_left, inside_bottom, radius);
CGPathAddLineToPoint(retPath, NULL, outside_left, inside_top);
CGPathAddArcToPoint(retPath, NULL, outside_left, outside_top, innerRect.origin.x, outside_top, radius);
CGPathCloseSubpath(retPath);
return retPath;
}
You're doing way too much work here. Just use UIBezierPath's +bezierPathWithRoundedRect:cornerRadius: and – appendPath: methods.
CGRect rect1, rect2;
CGFloat radius;
// fill in the values you want for the rects and the radius
UIBezierPath *result =
[UIBezierPath bezierPathWithRoundedRect: rect1 cornerRadius:radius];
[result appendPath:
[UIBezierPath bezierPathWithRoundedRect: rect2 cornerRadius:radius];
// result is now a path comprising both of the roundrects. You can fill it with a gradient like any other path.
You have some options:
Construct a new path that forms the union of the two separate paths without intersections. Core Graphics would not help you with this approach, it does not provide boolean path operations.
Use a transparency layer to draw both shapes (one after each other). This would fix the overlapping area but it wouldn't work with gradients.
Create a bitmap context to create a mask. Draw both shapes into the mask. Then clip to the mask in your original context and draw a rect over the bounding box of your shape.
I have the following UIView custom class:
#implementation BackgroundView
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
float r = (255.0)/(255.0);
float g = (165.0)/(255.0);
float b = (0.0)/(255.0);
float rr = (238.0)/(255.0);
float gg = (118.0)/(255.0);
float bb = (0.0)/(255.0);
CGFloat components[8] = { r, g, b, 1.0, // Start color
rr, gg, bb, 1.0 }; // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, midCenter, 0);
CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace);
}
#end
Basically, it's supposed to make an orange gradient. The values "r", "g", and "b" are for a light orange, and the values "rr", "gg", "bb" are for a dark orange. But it doesn't work. Only the top of the frame is orange, and the bottom of the frame is black. I can't seem to get rid of the black.
What values would I use for an orange nice gradient. Can someone please look for mistakes? I can't find any. Thanks so much!
That's because you specify that the end point for your gradient is in middle of the currentBounds - see this line:
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
To get rid of the black make sure the y coordinate is at the bottom of your view.
Alternatively, you could use this code to extend your gradient after the end point
CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, midCenter, kCGGradientDrawsAfterEndLocation);
The gradient in question is Figure 8-5 from the Quartz 2D Programming Guide, "A radial gradient that varies between a point and a circle".
I'm trying to build a CGGradient object (not a CGShading object, which might be the problem) like so:
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat colors[] =
{
0, 0, 0, 0.9,
0, 0, 0, 0
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*sizeof(CGFloat)));
CGContextClipToRect(context, rect);
CGContextDrawRadialGradient(context, gradient, startCenter, startRadius, endCenter, endRadius, gradientDrawingOptions);
CGGradientRelease(gradient);
CGColorSpaceRelease(rgb);
Of course, that isn't exactly right -- the centre points and radii are correct, but the actual gradient doesn't look the same. I just wish Apple had provided the source code for each example! >:(
UPDATE: These color values add the shading on top of other content (drawing from a point out to a circle):
CGFloat colors[] =
{
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.75f
};
Using these color values is pretty close (drawing from a point out to a circle):
CGFloat colors[] =
{
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 0.75f
};
I am creating my own UITableViewCells with a gradient background. I have all the logic and drawing worked out, but one thing I want to fix is the "chunkiness" around the corners of my custom cell:
alt text http://grab.by/27SM
If you zoom in on the corners, you can see what I am talking about. Here is the code I a using to generate the cell:
CGContextRef c = UIGraphicsGetCurrentContext();
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef myGradient = nil;
CGFloat components[8] = TABLE_CELL_BACKGROUND;
CGContextSetStrokeColorWithColor(c, [[UAColor colorWithWhite:0.7 alpha:1] CGColor]);
CGContextSetLineWidth(c, 2);
CGContextSetAllowsAntialiasing(c, YES);
CGContextSetShouldAntialias(c, YES);
CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, minx, miny);
CGPathAddArcToPoint(path, NULL, minx, maxy, midx, maxy, kDefaultMargin);
CGPathAddArcToPoint(path, NULL, maxx, maxy, maxx, miny, kDefaultMargin);
CGPathAddLineToPoint(path, NULL, maxx, miny);
CGPathAddLineToPoint(path, NULL, minx, miny);
CGPathCloseSubpath(path);
// Fill and stroke the path
CGContextSaveGState(c);
CGContextAddPath(c, path);
CGContextClip(c);
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat mycomponents[8] = TABLE_CELL_BACKGROUND;
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
myGradient = CGGradientCreateWithColorComponents(myColorspace, mycomponents, locations, 2);
CGContextDrawLinearGradient(c, myGradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), 0);
CGContextRestoreGState(c);
CGContextAddPath(c, path);
CGContextStrokePath(c);
What can I do to smooth the edges while keeping a consistent edge thickness across all cells?
Your line width is set to 2 points. What's happening is that your code is calculating your bounding rect without understanding the width of the line. The result is that for every straight segment of your shape, only half of the stroke's width is visible. On the arc, the full stroke width is visible.
Here's the relevant segment of code from my app, Funversation, to draw the playing cards with rounded corners similar to what you have.
CGRect rect = [self bounds];
rect.size.width -= lineWidth;
rect.size.height -= lineWidth;
rect.origin.x += lineWidth / 2.0;
rect.origin.y += lineWidth / 2.0;
Add that before your calculation for minx, midx, maxx, etc. and the strokes for your shape should be uniform.
The other way to make the stroke consistent would be to move the AddPath and StrokePath calls up above the RestoreGState call—that is, stroke while clipped.
For a truly 2-pt-wide stroke with this solution, simply double the line width you put in the graphics state (i.e., set it to 4 pt), since half of it will be clipped out.
I am trying to get each pixel point of a Square drawn using Core graphics.Here by making the stroke color black color,I am drawing the Square.Please give me an idea how I will get all pixel point on which this square is drawn.
- (void)drawRect:(CGRect)rect
{
CGMutablePathRef path = CGPathCreateMutable();
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPathMoveToPoint(path, NULL, 30.0f, 30.0f);
CGPathAddLineToPoint(path, NULL, 130.0f, 30.0f);
CGPathAddLineToPoint(path, NULL, 130.0f, 130.0f);
CGPathAddLineToPoint(path, NULL, 30.0f, 130.0f);
CGPathCloseSubpath(path);
CGPathRetain(path);
CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
CGContextSetStrokeColorWithColor(ctx,[UIColor blackColor].CGColor);
CGContextSetLineWidth(ctx, 2.0);
CGContextSaveGState(ctx);
CGContextAddPath(ctx, path);
CGContextRestoreGState(ctx);
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx);
CGContextRestoreGState(ctx);
[self setNeedsDisplay];
CGPathRelease(path);
}
Why are you doing all this work instead of just using CGContextFillRect() and CGContextStrokeRect()?
Your code above can be simplified to:
CGRect r = CGRectMake(30.0, 30.0, 100.0, 100.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, CGColorGetConstantColor(kCGColorClear));
CGContextFillRect(ctx, r);
CGContextSetLineWidth(ctx, 2.0);
CGContextSetStrokeColorWithColor(ctx, CGColorGetConstantColor(kCGColorBlack));
CGContextStrokeRect(ctx, r);
Also, never send -setNeedsDisplay within your -drawRect: method. You'll get an infinite loop.
I needed to do something similar for my iPhone app and while I realize it is a little late, I decided to respond anyway.
First, initialize a mutable array (points).
Next, find the minimum X and Y coordinates for your CGRect.
Do the same for the maximum.
Find the difference between the minimum and the maximum.
Now, create a for loop like the one below:
for(int x = minX; x<diffX+minX; x++){
for(int y = minY; y<diffY+minY; y++){
[points addObject:[NSValue valueWithCGPoint:CGPointMake(x,y)]];
}
}
You can now access your points through the points array.