I am drawing annotations on a view. The approach i am using to draw star is below.
-(void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGFloat xCenter = rect.size.width / 2;
CGFloat yCenter = rect.size.height / 2;
float width;
if(rect.size.width > rect.size.height) {
width = rect.size.height;
} else {
width = rect.size.width;
}
double r = width / 2.0;
float flip = -1.0;
double theta = 2.0 * M_PI * (2.0 / 5.0); // 144 degrees
CGContextMoveToPoint(context, xCenter, r*flip+yCenter);
for (NSUInteger k=1; k<5; k++)
{
float x = r * sin(k * theta);
float y = r * cos(k * theta);
CGContextAddLineToPoint(context, x+xCenter, y*flip+yCenter);
}
CGContextSetStrokeColorWithColor(context, self.color.CGColor);
CGContextClosePath(context);
CGContextStrokePath(context);
}
I want to have just borders of star removing other inner lines is there any way to achieve that? (apart from using moveToPoint and addLine methods)
use this code:
-(void)drawRect:(CGRect)rect
{
int aSize = 100.0;
float color[4] = { 0.0, 0.0, 1.0, 1.0 }; // Blue
CGColorRef aColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), color);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, aSize);
CGFloat xCenter = 100.0;
CGFloat yCenter = 100.0;
float w = 100.0;
double r = w / 2.0;
float flip = -1.0;
CGContextSetFillColorWithColor(context, aColor);
CGContextSetStrokeColorWithColor(context, aColor);
double theta = 2.0 * M_PI * (2.0 / 5.0); // 144 degrees
CGContextMoveToPoint(context, xCenter, r*flip+yCenter);
for (NSUInteger k=1; k<5; k++)
{
float x = r * sin(k * theta);
float y = r * cos(k * theta);
CGContextAddLineToPoint(context, x+xCenter, y*flip+yCenter);
}
CGContextClosePath(context);
CGContextFillPath(context);
}
This will create 1 blue star
With use of the UIBezierPath you can work with that in Swift 4.
let polygonPath = UIBezierPath()
let xCenter: CGFloat = 0
let yCenter: CGFloat = 0
let w = CGFloat(300)
let r = w / 2.0
let flip: CGFloat = -1.0 // use this to flip the figure 1.0 or -1.0
let polySide = CGFloat(5)
let theta = 2.0 * Double.pi * Double(2.0 / polySide)
polygonPath.move(to: CGPoint(x: xCenter, y: r * flip + yCenter))
for i in 1..<Int(polySide) {
let x: CGFloat = r * CGFloat( sin(Double(i) * theta) )
let y: CGFloat = r * CGFloat( cos(Double(i) * theta) )
polygonPath.addLine(to: CGPoint(x: x + xCenter, y: y * flip + yCenter))
}
polygonPath.close()
You can draw star by using this code:
UIBezierPath* starPath = UIBezierPath.bezierPath;
[starPath moveToPoint: CGPointMake(90, 31)];
[starPath addLineToPoint: CGPointMake(98.11, 42.06)];
[starPath addLineToPoint: CGPointMake(111.87, 45.86)];
[starPath addLineToPoint: CGPointMake(103.12, 56.49)];
[starPath addLineToPoint: CGPointMake(103.52, 69.89)];
[starPath addLineToPoint: CGPointMake(90, 65.4)];
[starPath addLineToPoint: CGPointMake(76.48, 69.89)];
[starPath addLineToPoint: CGPointMake(76.88, 56.49)];
[starPath addLineToPoint: CGPointMake(68.13, 45.86)];
[starPath addLineToPoint: CGPointMake(81.89, 42.06)];
[starPath closePath];
[UIColor.grayColor setFill];
[starPath fill];
Related
I used CGContext to draw dashed lines in iOS.
My code is as follow.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGFloat dashes[] = {5,5};
CGContextSetLineDash(context, 0, dashes, 2);
float startx = x_percentileValues[0];
float starty = orgy-numskipPixels_v*(3-minWeight);
for(int i=0; i<[MeasuredInfos.retval count]; i++) {
float x = numskipPixels_h*hoursBetweenDates*3+orgx;
float y = orgy-([[MeasuredInfos.retval objectAtIndex: i] getWeight] - minWeight)*numskipPixels_v;
CGContextMoveToPoint(context, startx, starty);
CGContextAddLineToPoint(context, x, y);
CGContextStrokePath(context);
startx = x;
starty = y;
}
It is fine if the slope of the line is steep. If not steep, the dashed line has problem as shown in the attached pictures.
I checked in this link, nothing much discussion for the CGContextSetLineDash.
I found the problem. The problem is that I used two CGContextRef in two separate functions. One is in
- (void)drawRect:(CGRect)rect {
//Get the CGContext from this view
CGContextRef context = UIGraphicsGetCurrentContext();
//Set the stroke (pen) color
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
//Set the width of the pen mark
CGContextSetLineWidth(context, 2.0);
float startx = x_percentileValues[0];
float starty = orgy-numskipPixels_v*(3-minWeight);
for(int i=0; i<[MeasuredInfos.retval count]; i++) {
float x = numskipPixels_h*hoursBetweenDates*3+orgx;
float y = orgy-([[MeasuredInfos.retval objectAtIndex: i] getWeight] - minWeight)*numskipPixels_v;
CGContextMoveToPoint(context, startx, starty);
CGContextAddLineToPoint(context, x, y);
CGContextStrokePath(context);
startx = x;
starty = y;
[self drawStar_atX:x andatY:y];
}
}
Another one in drawStar_atX
-(void)drawStar_atX:(float)xCenter andatY:(float)yCenter
{
int aSize = 5.0;
CGFloat color[4] = { 1.0, 0.0, 0.0, 1.0 }; // Red
CGColorRef aColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), color);
CGContextRef context1 = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context1, aSize);
float w = 15.0;
double r = w / 2.0;
float flip = -1.0;
CGContextSetFillColorWithColor(context1, aColor);
CGContextSetStrokeColorWithColor(context1, aColor);
double theta = 2.0 * M_PI * (2.0 / 5.0); // 144 degrees
CGContextMoveToPoint(context1, xCenter, r*flip+yCenter);
for (NSUInteger k=1; k<5; k++)
{
float x = r * sin(k * theta);
float y = r * cos(k * theta);
CGContextAddLineToPoint(context1, x+xCenter, y*flip+yCenter);
}
CGContextClosePath(context1);
CGContextFillPath(context1);
return;
}
I called drawStar_atX from drawRect. context and context1 in two functions have some problems and start from the second segment the dashed line's width becomes bigger. Actually they are declared in different functions with different widths, shouldn't have any relationship. Now I solved the problem as context and context1 has same width. Still learning why they have some relations.
I am drawing a curved line using cubed bezier curve (UIBezierPath class) using two control points that are in opposite corners of our points. I need to draw it with gradient that depends on y value. I appreciate any help because now I've stucked in. Here is my code:
CGPoint point1 = CGPointMake(50, 70);
CGPoint point2 = CGPointMake(70, 75);
bool drawACurve;
drawACurve = true;
float tangent;
if ((int)(point2.y - point1.y) == 0) {
tangent = 0.0f;
drawACurve = false;
}
else {
tangent = (point2.x - point1.x) / (point2.y - point1.y);
}
UIBezierPath *path1 = [UIBezierPath bezierPath];
[path1 setLineWidth:1.0];
[[UIColor blueColor] set];
if (drawACurve)
{
// Draw a curve
float factor;
CGPoint controlPoint1;
CGPoint controlPoint2;
factor = MAX_FACTOR;
if (fabs(tangent) >= 1.0 && fabs(tangent) < TANGENT_BOUND_FOR_LINE)
factor = MIN_FACTOR;
if (tangent > 0) {
controlPoint1 = CGPointMake(point2.x - point2.x * factor, point1.y - point1.y * factor);
controlPoint2 = CGPointMake(point1.x + point1.x * factor, point2.y + point2.y * factor);
}
else {
// For tangent less than zero
controlPoint1 = CGPointMake(point2.x - point2.x * factor, point1.y + point1.y * factor);
controlPoint2 = CGPointMake(point1.x + point1.x * factor, point2.y - point2.y * factor);
}
[path1 moveToPoint:point1];
[path1 addCurveToPoint:point2 controlPoint1:controlPoint1 controlPoint2:controlPoint2];
}
else
{
// Draw a line
[path1 moveToPoint:point1];
[path1 addLineToPoint:point2];
}
[path1 stroke];
If I've understood you correctly, here's one solution. I've drawn the path using a simple blue-to-green gradient, varying in the y-direction. I've commented the code to explain what it's doing.
CGPoint point1 = CGPointMake(50, 70);
CGPoint point2 = CGPointMake(70, 75);
bool drawACurve;
drawACurve = true;
float tangent;
if ((int)(point2.y - point1.y) == 0) {
tangent = 0.0f;
drawACurve = false;
}
else {
tangent = (point2.x - point1.x) / (point2.y - point1.y);
}
UIBezierPath *path1 = [UIBezierPath bezierPath];
[path1 setLineWidth:1.0];
if (drawACurve)
{
// Draw a curve
float factor;
CGPoint controlPoint1;
CGPoint controlPoint2;
factor = MAX_FACTOR;
if (fabs(tangent) >= 1.0 && fabs(tangent) < TANGENT_BOUND_FOR_LINE)
factor = MIN_FACTOR;
if (tangent > 0) {
controlPoint1 = CGPointMake(point2.x - point2.x * factor, point1.y - point1.y * factor);
controlPoint2 = CGPointMake(point1.x + point1.x * factor, point2.y + point2.y * factor);
}
else {
// For tangent less than zero
controlPoint1 = CGPointMake(point2.x - point2.x * factor, point1.y + point1.y * factor);
controlPoint2 = CGPointMake(point1.x + point1.x * factor, point2.y - point2.y * factor);
}
[path1 moveToPoint:point1];
[path1 addCurveToPoint:point2 controlPoint1:controlPoint1 controlPoint2:controlPoint2];
}
else
{
// Draw a line
[path1 moveToPoint:point1];
[path1 addLineToPoint:point2];
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // You could choose another color space.
CGFloat locations[2] = {0.f, 1.f}; // Locations for a simple linear gradient with a start color and end color at either end of the drawing points.
CGFloat colorComponents[8] = {0.f, 0.f, 1.f, 1.f, // Start blue
0.f, 1.f, 0.f, 1.f}; // Finish green
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colorComponents, locations, 2); // Create the gradient. Can also be created with CGGradientCreateWithColors().
CGColorSpaceRelease(colorSpace);
CGPathRef outerPath = CGPathCreateCopyByStrokingPath(path1.CGPath, NULL, path1.lineWidth, path1.lineCapStyle, path1.lineJoinStyle, path1.miterLimit); // This function creates a path that outlines another, using various stroke options. Pass in the stroke options from path1.
// From the docs: The new path is created so that filling the new path draws the same pixels as stroking the original path.
CGContextRef context = UIGraphicsGetCurrentContext(); // Get the current graphics context.
CGContextAddPath(context, outerPath); // Add the new path.
CGContextClip(context); // Clip the context to the path.
CGRect boundingBox = CGPathGetBoundingBox(outerPath); // I'm just doing this to get the startPoint and endPoint for the gradient drawing.
CGPathRelease(outerPath);
CGPoint startPoint = boundingBox.origin;
CGPoint endPoint = CGPointMake(boundingBox.origin.x, CGRectGetHeight(boundingBox)); // Different from startPoint in y value, as requested.
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); // Draw the gradient.
CGGradientRelease(gradient);
I'm trying to make high-resolution graphics dynamically. Unfortunately when I make arcs they look really fuzzy.
Here's my code
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect bounds = [self bounds];
CGPoint center = CGPointMake(bounds.size.width / 2.0, bounds.size.height / 2.0);
CGFloat lineWidth = 30.0;
CGFloat innerRadius = (bounds.size.width / 2.0) - lineWidth;
CGFloat outerRadius = innerRadius + lineWidth;
CGFloat startAngle = -((float)M_PI / 2.0);
CGFloat endAngle = ((self.percent / 100.0) * 2 * (float)M_PI) + startAngle;
UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
processBackgroundPath.lineWidth = lineWidth;
CGFloat radius = (self.bounds.size.width - lineWidth) / 2.0;
CGFloat fullAngle = (2.0 * (float)M_PI) + startAngle;
[processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:fullAngle clockwise:YES];
[[UIColor whiteColor] set];
[processBackgroundPath stroke];
CGMutablePathRef progressPath = CGPathCreateMutable();
CGPathMoveToPoint(progressPath, NULL, center.x, center.y - innerRadius);
CGPathAddArc(progressPath, NULL, center.x, center.y, innerRadius, startAngle, endAngle, YES);
CGPathAddArc(progressPath, NULL, center.x, center.y, outerRadius, endAngle, startAngle, NO);
CGPathCloseSubpath(progressPath);
UIColor *aColor = [UIColor colorWithRed:0.941 green:0.776 blue:0.216 alpha:1.0];
[aColor setFill];
CGContextAddPath(context, progressPath);
CGContextFillPath(context);
CGPathRelease(progressPath);
}
What do I need to do to make the donut more crisp?
I am trying to create the circles below in my iOS app. I know how to make the circles but am not entirely sure on how to get the points along the arc. It has to be in code not an image. Below is also the code I currently have.
- (void)drawRect:(CGRect)rect
{
CGPoint point;
point.x = self.bounds.origin.x + self.bounds.size.width/2;
point.y = self.bounds.origin.y + self.bounds.size.height/2;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGRect circle = CGRectMake(point.x/2,point.y-point.x/2,point.x,point.x);
CGContextAddEllipseInRect(context, circle);
CGContextStrokePath(context);
for (int i = 0; i<8; i++) {
CGRect circleMini = CGRectMake(??????,??????,point.x/4,point.x/4);
CGContextAddEllipseInRect(context, circleMini);
CGContextStrokePath(context);
}
}
UPDATED TO ANSWER
float cita = 0;
for (int i = 0; i<8; i++) {
CGPoint pointCir = CGPointMake(point.x/2 + radius * cos(cita) , (point.y-point.x/2) + radius * sin(cita) );
CGRect circleMini = CGRectMake(pointCir.x,pointCir.y,radius/4,radius/4);
CGContextAddEllipseInRect(context, circleMini);
CGContextStrokePath(context);
cita += M_PI / 4.0;
}
If (x,y) is the center and r is the radius of your big circle, the center of the i-th external circle will be:
center(i) = ( x + r * cos(cita) , y + r * sin(cita) )
Start cita in 0 and increment it PI/4 radians for the next circle (or 45 degrees)
Working implementation
CGFloat cita = 0;
CGFloat bigCircleRadius = point.x / 2.0;
CGFloat smallCircleRadius = bigCircleRadius / 4.0;
for (int i = 0; i < 8; i++) {
CGPoint smallCircleCenter = CGPointMake(point.x + bigCircleRadius * cos(cita) - smallCircleRadius/2.0 , point.y + bigCircleRadius * sin(cita) - smallCircleRadius / 2.0 );
CGRect smallCircleRect = CGRectMake(smallCircleCenter.x,smallCircleCenter.y,smallCircleRadius,smallCircleRadius);
CGContextAddEllipseInRect(context, smallCircleRect);
CGContextStrokePath(context);
cita += M_PI / 4.0;
}
Edit: Added implementation and renamed variables.
- (void)drawRect:(CGRect)rect
{
const NSUInteger kNumCircles = 8u;
CGFloat height = CGRectGetHeight(rect);
CGFloat smallCircleRadius = height / 10.0f;
CGRect bigCircleRect = CGRectInset(rect, smallCircleRadius / 2.0f, smallCircleRadius / 2.0f);
CGFloat bigCircleRadius = CGRectGetHeight(bigCircleRect) / 2.0f;
CGPoint rectCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0f);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextAddEllipseInRect(context, bigCircleRect);
CGContextStrokePath(context);
CGFloat alpha = 0;
for (NSUInteger i = 0; i < kNumCircles; i++)
{
CGPoint smallCircleCenter = CGPointMake(rectCenter.x + bigCircleRadius * cos(alpha) - smallCircleRadius/2.0f , rectCenter.y + bigCircleRadius * sin(alpha) - smallCircleRadius / 2.0f );
CGRect smallCircleRect = CGRectMake(smallCircleCenter.x,smallCircleCenter.y,smallCircleRadius,smallCircleRadius);
CGContextAddEllipseInRect(context, smallCircleRect);
CGContextStrokePath(context);
alpha += M_PI / (kNumCircles / 2.0f);
}
}
I'm trying to draw a simple speech bubble using the following code:
#implementation SpeechBubbleView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGPoint triangleHeadPoint = CGPointMake(0, rect.size.height/2.0);
float triangleHeight = 5;
float triangleWidth = 10;
float maxX = rect.size.width;
float minX = 0.0;
float maxY = rect.size.height;
float minY = 0.0;
float archTangentLine = 6.0;
float archRadius = archTangentLine;
float strokeWidth = 1.0;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, strokeWidth);
CGContextBeginPath(context);
float currentX = triangleHeadPoint.x;
float currentY = triangleHeadPoint.y;
CGContextMoveToPoint(context, currentX, currentY);
currentX += triangleWidth;
currentY += (triangleHeight / 2.0);
CGContextAddLineToPoint(context, currentX, currentY);
currentY = maxY - archTangentLine;
CGContextAddLineToPoint(context, currentX, currentY);
currentY += archTangentLine;
CGContextAddArcToPoint(context, currentX, currentY, currentX + archTangentLine, currentY, archRadius);
currentX = maxX - archTangentLine;
CGContextAddLineToPoint(context, currentX, currentY);
currentX += archTangentLine;
CGContextAddArcToPoint(context, currentX, currentY, currentX, currentY - archTangentLine, archRadius);
currentY = minY + archTangentLine;
CGContextAddLineToPoint(context, currentX, currentY);
currentY -= archTangentLine;
CGContextAddArcToPoint(context, currentX, currentY, currentX - archTangentLine, currentY, archRadius);
currentX = minX + triangleWidth + archTangentLine;
CGContextAddLineToPoint(context, currentX, currentY);
currentX -= archTangentLine;
CGContextAddArcToPoint(context, currentX, currentY, currentX, currentY + archTangentLine, archRadius);
currentY = triangleHeadPoint.y - (triangleHeight / 2.0);
CGContextAddLineToPoint(context, currentX, currentY);
CGContextClosePath(context);
[[UIColor whiteColor] setFill];
[[UIColor blackColor] setStroke];
CGContextDrawPath(context, kCGPathFillStroke);
}
#end
The result of this code is the following (not so pretty) image:
The drawing isn't as smooth as I was expecting it to be, the left side border is wider than the top, right and bottom sides, and the corners are wider than the adjacent lines.
I changed the fill color to black just to check and the result is as I was expecting:
Why does the border have a variable width? Is there a way to fix this and have a smooth border around the view?
Thanks in advance.
For future reference, I have found a way to improve the look of the border which will result in the following picture:
I simply made the border width very small (I used 0.2f) using the function CGContextSetLineWidth(context, 0.2f). And then added a shadow to the layer as following:
self.layer.shadowColor = [[UIColor blackColor] CGColor];
self.layer.shadowOffset = CGSizeMake(1.5f, 1.5f);
self.layer.shadowOpacity = 0.8f;
self.layer.shadowRadius = 1.0f;
And that's it!