Below is the code which draws the Arc from M_PI/2 to M_PI.
CGContextRef contet=UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, CGRectGetMidX(rect), CGRectGetMidY(rect), CGRectGetMidX(rect), M_PI/2.0,M_PI, YES);
CGContextAddPath(contet, path);
CGContextSetStrokeColorWithColor(contet, [UIColor redColor].CGColor);
CGContextStrokePath(content);
Below is the drawing of the above code.
I was expecting it to draw it in the empty or missing part , as its clockwise. I referred to this answer Why does giving addArcWithCenter a startAngle of 0 degrees make it start at 90 degrees? but, I don't know where I am wrong.
Because,in Core Graphics,the origin is at left/bottom
Flipping horizontally changes clockwise to anti clockwise and vice versa
You just need to change clockwise to NO to get what you want
You should just pay more attention to the method you used.
CGPathAddArc(path, NULL, CGRectGetMidX(rect), CGRectGetMidY(rect), CGRectGetMidX(rect), M_PI/2.0,M_PI, NO);
Just change the last parameter to NO, you will get what you want, it can control the drawing direction.
Related
First off, here's an image of my current situation.
To accomplish this I am using a subclassed UIView with the following method:
-(void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextBeginPath(ctx);
CGContextMoveToPoint (ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); // top left
CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMidY(rect)); // mid right
CGContextAddLineToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); // bottom left
CGContextClosePath(ctx);
CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);
CGContextFillPath(ctx);
}
My ultimate goal is to clip the colored regions so that only the neon green remains. (I'll flip the colors later). I want it to look like a pie chart. I'm guessing there's a way to clip the colored corners out but I have no clue. CGContextClip(ctx) doesn't work.
I am using the Pixate framework as well. If anyone knows a way to accomplish a triangle shape with Pixate that would be even better.
The easiest way to clip a UIView to a path is to create a CAShapeLayer, add a path to the layer, and add the shape layer as a mask of your view's layer.
Or with a CGPathRef, essentially the same thing but at core foundation level
eg..
{
// before existing drawing code..
CGMutablePathRef clipTriangle = CGPathCreateMutable();
CGPathMoveToPoint (clipTriangle, nil, startX, startY);
CGPathAddLineToPoint(clipTriangle, nil, secondPointX, secondPointY);
CGPathAddLineToPoint(clipTriangle, nil, thirdPointX, thirdPointY);
CGPathCloseSubpath(clipTriangle);
CGContextSaveGstate(ctx);
CGContextAddPath(ctx, clipTriangle); /// this is the essential line I left out, we drew the triangle and forgot to use it, clipping to nothing instead, sorry mate
CGContextClip(ctx);
//draw stuff that you wanted clipped here...
CGContextRestoreGstate(ctx);
CGPathRelease(clippingTriangle); //CFstuff still needs manual memory management regardless of ARC
}
I'm having an issue when drawing an arc inside of my drawing function inside of a CALayer subclass. The implementation for that drawing function is as follows:
-(void)drawInContext:(CGContextRef)ctx
{
CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
CGFloat radius = MIN(center.x, center.y);
CGContextBeginPath(ctx);
CGContextAddArc(ctx, center.x, center.y, radius, DEG2RAD(0), DEG2RAD(90), YES);
CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(ctx, 5);
CGContextDrawPath(ctx, kCGPathStroke);
}
Nothing too groundbreaking here, but the weird thing is that it's drawing the arc counterclockwise even though I specified that it shoud be clockwise. Conversely, when I specify NO for the clockwise parameter, it draws the arc clockwise. I did some research into flipped coordinate systems and figured that might be what the issue here is, but I didn't want to just hardcode the opposite bool parameter of what I meant. Any suggestions on how to fix this?
Thanks!
This is indeed due to the flipped coordinate system of UIKit vs. Quartz. From the docs for CGContext:
The clockwise parameter determines the direction in which the arc is created; the actual direction of the final path is dependent on the current transformation matrix of the graphics context. For example, on iOS, a UIView flips the Y-coordinate by scaling the Y values by -1. In a flipped coordinate system, specifying a clockwise arc results in a counterclockwise arc after the transformation is applied.
You can alleviate this in your code by using the transformation matrix to flip your context:
CGContextTranslateCTM(ctx, 0.0, self.bounds.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
You probably want to flip it back when you are finished with your drawing i.e.
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx, 0.0, self.bounds.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
// Draw...
CGContextRestoreGState(ctx);
I want to display a complex UI element as a kinda donut with sectors, so each sector could be represented as a single UIView.
Is there any way to transform the rectangular UIView into an arc to fit into donut contour? Or that's impossible?
I'm a total iOS noob and would be very appreciative if you pointed me to the right documentation.
UIView's are always rectangular unless you apply an affine transform to it (but I still don't think you can get an arc shape). I think your best bet would be to draw the arc inside the UIView by subclassing the UIView and implementing drawRect or using CAShapeLayer.
Without knowing more about exactly what your trying to do, I'm going to go out on a limb and suggest you use CGDrawing methods to draw your donut segments in a rectangular view.
To do that you would use the following in particular:
CGPathAddArc()
CGPathAddLineToPoint()
CGPathMoveToPoint()
This code draws filled arcs:
-(void)drawFilledArc:(CGContextRef)context innerRadius:(CGFloat)rIn outterRadius (CGFloat)rOut
startAngle:(double)startAng endAngle:(double)endAng drawColor:(UIColor *)color
{
float x1, y1;
CGFloat centerX = originX;
CGFloat centerY = originY;
CGContextSaveGState(context);
CGMutablePathRef path = CGPathCreateMutable();
CGContextSetLineWidth(context, lineWidth);
getChartWheelCoord(centerX, centerY, startAng, rIn, &x1, &y1);
CGPathMoveToPoint(path, NULL, x1, y1);
getChartWheelCoord(centerX, centerY, startAng, rOut, &x1, &y1);
CGPathAddLineToPoint(path, NULL, x1, y1);
CGPathAddArc(path, NULL, centerX, centerY, rOut, radians(-startAng), radians(-endAng), YES);
getChartWheelCoord(centerX, centerY, endAng, rIn, &x1, &y1);
CGPathAddLineToPoint(path, NULL, x1, y1);
CGPathAddArc(path, NULL, centerX, centerY, rIn, radians(-endAng), radians(-startAng), NO);
CGPathCloseSubpath(path);
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathFillStroke);
CGPathRelease(path);
CGContextRestoreGState(context);
}
Well You can achieve what you want in following ways.
1 If your UI Element has not to be answerable to Click or touch Events , then you can draw it
Using Core-Graphics.
2 If your UI Element has to respond to user-interaction events then you can create a UIView
and add as many custom-buttons as you want as subview to the UIview along with the arc shaped or sector shaped images(Images for Custom Buttons).
I am trying to draw a CGPath that has a stroke for it's stroke.
Basically I want a draw a line using CGPath. I then want to go back and draw lines on both sides of the last CGPath giving it the effect that it is outlines.
This line can bend and turn in any way but I always need the two lines on the outside to follow.
EDIT: I need to be able to make the middle of the line transparent but the outlines solid black.
Use CGPathCreateCopyByStrokingPath to create a new path by stroking your old path at some width. Then draw your new path using kCGPathFillStroke.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 50, 50);
CGPathAddLineToPoint(path, NULL, 200, 200);
CGPathRef thickPath = CGPathCreateCopyByStrokingPath(path, NULL, 10, kCGLineCapButt, kCGLineJoinBevel, 0);
CGContextAddPath(context, thickPath);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetLineWidth(context, 3);
CGContextDrawPath(context, kCGPathFillStroke);
CGPathRelease(thickPath);
CGPathRelease(path);
}
The simplest solution would just be to stroke the path twice. First with black at a bigger stroke width and then stroke the same path again using the blue color with a slightly smaller stroke width.
Edit:
If I remember correctly you can use CGPathCreateCopyByStrokingPath(...) to create a new path that you then can both stroke and fill. Then you could use semi-transparent colors.
From the documentation:
CGPathCreateCopyByStrokingPath
Creates a stroked copy of another path.
CGPathRef CGPathCreateCopyByStrokingPath(
CGPathRef path,
const CGAffineTransform *transform,
CGFloat lineWidth,
CGLineCap lineCap,
CGLineJoin lineJoin,
CGFloat miterLimit
);
Parameters
path
The path to copy.
transform
A pointer to an affine transformation matrix, or NULL if no transformation is needed. If specified, Quartz applies the transformation to elements of the converted path before adding them to the new path.
lineWidth
The line width to use, in user space units. The value must be greater than 0.
lineCap
A line cap style constant—kCGLineCapButt (the default), kCGLineCapRound, or kCGLineCapSquare. See “CGLineCap”.
lineJoin
A line join value—kCGLineJoinMiter (the default), kCGLineJoinRound, or kCGLineJoinBevel. See “CGLineJoin”.
miterLimit
The miter limit to use.
I'm able to use CGContextDrawRadialGradient to make a sphere that does an alpha fade to UIColor.clearColor and it works.
However, I'm trying to do this type of thing:
While placing some strategic spheres around makes for an interesting effect (similar to LED backlights in strategic places), I would love to get a true glow. How can I draw a glow around a rounded rectangle in drawRect?
You can create a glow effect around any path using CGContextSetShadowWithColor, but you don't get precise control over the appearance. In particular, the default shadow is fairly light:
And the only way I know of to make it darker is to draw it again over itself:
Not optimal, but it approximates what you want pretty well.
Those images were generated by the following drawRect:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
int padding = 20;
CGPathMoveToPoint(path, NULL, padding, padding);
CGPathAddLineToPoint(path, NULL, rect.size.width - padding, rect.size.height / 2);
CGPathAddLineToPoint(path, NULL, padding, rect.size.height - padding);
CGContextSetShadowWithColor(context, CGSizeZero, 20, UIColor.redColor.CGColor);
CGContextSetFillColorWithColor(context, UIColor.blueColor.CGColor);
CGContextAddPath(context, path);
CGContextFillPath(context);
// CGContextAddPath(context, path);
// CGContextFillPath(context);
CGPathRelease(path);
}
One thing to bear in mind is that rendering fuzzy shadows is fairly expensive, which may or may not be a problem depending on how often your views are redrawn. If the shadows don't need to animate, consider rendering them to a UIImage once and just displaying the result in a UIImageView.