using multiple colours for stroking context - ios

i'm drawing a graph and i want the the ascending lines to be in green and the descending lines in red. i tried the following:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGPoint previousPoint = CGPointMake(0.0, 0.0);
//graph the points
for (int i = 0; i < self.numberOfS; i++) {
CGPoint currentPoint = CGPointMake(150+(40*[[[self.grades objectAtIndex:i] s] doubleValue]), 59+(40*(i+1)));
if (previousPoint.x != 0.0f) {
if (previousPoint.x > currentPoint.x) {
CGContextMoveToPoint(context, previousPoint.x, previousPoint.y);
CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
CGContextAddLineToPoint(context, 150+(40*[[[self.grades objectAtIndex:i] semesterGPA] doubleValue]), 59+(40*(i+1)));
} else{
CGContextMoveToPoint(context, previousPoint.x, previousPoint.y);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextAddLineToPoint(context, 150+(40*[[[self.grades objectAtIndex:i] semesterGPA] doubleValue]), 59+(40*(i+1)));
}
}
previousPoint.x = currentPoint.x;
previousPoint.y = currentPoint.y;
}
but it's not working, everything is in red. how can i fix this ?

Ok solved the problem, i just entered this line of code:
CGContextStrokePath(context);

Related

Why is Core Graphics drawing this outline?

For some reason Core Graphics is drawing an extra outline around my path. Here is some sample code.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSaveGState(context);
float startRadius = 71.0;
float startAngle = 135, endAngle = 405;
endAngle = startAngle + (endAngle - startAngle)*drawValue;
int clockwise = 0;
if(isTargetFiller) {
if(drawValue < 0.0)
clockwise = 1;
startAngle = 270;
endAngle = 405;
endAngle = startAngle + (endAngle - startAngle)*drawValue;
}
CGPoint center = CGPointMake(75, 75);
CGContextMoveToPoint(context, center.x, center.y);
CGContextAddArc(context, center.x, center.y, startRadius, DEG_2_RAD(startAngle),
DEG_2_RAD(endAngle), clockwise);
CGContextClosePath(context);
CGContextClip(context);
UIImage *bigImage = [UIImage imageNamed:#"CTracker_Full.png"];
CGPoint topLeftOffset = CGPointMake(
(self.bounds.size.width - bigImage.size.width) / 2,
(self.bounds.size.height - bigImage.size.height) / 2);
[bigImage drawAtPoint: topLeftOffset blendMode:kCGBlendModeNormal alpha:1.0];
CGContextSetBlendMode (context, kCGBlendModeMultiply);
float green = 1.0;
float red = 0.0;
if(value > 0.5) {
green = 1.0 - (value - 0.5) / 0.5;
red = (value - 0.5) / 0.5;
}
UIColor *tintColor = [UIColor colorWithRed:red green:green blue:0.0 alpha:1.0];
CGContextSetFillColor(context, CGColorGetComponents(tintColor.CGColor));
CGContextFillRect(context, rect);
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
[bigImage drawAtPoint:topLeftOffset blendMode:kCGBlendModeDestinationIn
alpha:1.0];
Here is what it is currently drawing:
As you can see, there is an outline around the actual shape that leads to the center of the image.
Figured it out myself right after posting this. Hopefully this will help someone else.
Changed the last little bit of code to:
UIColor *tintColor = [UIColor colorWithRed:red green:green blue:0.0 alpha:1.0];
CGContextSetFillColor(context, CGColorGetComponents(tintColor.CGColor));
CGContextFillRect(context, rect);
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
[bigImage drawAtPoint:topLeftOffset blendMode:kCGBlendModeDestinationIn alpha:1.0];

how to join line with round corder

I have three points, A,B and C, the code to link the three points as following
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
NSArray *points = #[ [NSValue valueWithCGPoint:CGPointMake(10.0f, 15.0f)],
[NSValue valueWithCGPoint:CGPointMake(100.0f, 170.0f)],
[NSValue valueWithCGPoint:CGPointMake(190.0f, 100.0f)],
];
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 2.0f);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
for(int i = 0;i<points.count; ++i){
NSValue *pointValue = [points objectAtIndex:i];
CGPoint point = [pointValue CGPointValue];
if ( i == 0) {
CGContextMoveToPoint(context, point.x, point.y);
} else {
CGContextAddLineToPoint(context, point.x, point.y);
}
}
CGContextStrokePath(context);
}
However, the junction of two lines is an angle, I need to join point B with a round corder. like following image:
how to do it?
What you have is 2 straight lines, what you're asking for is (possibly 2 straight lines, joined by) an arc / bezier curve.
Look at using CGPathAddArcToPoint or CGPathAddCurveToPoint or CGPathAddQuadCurveToPoint.

Memory issues with Core Graphics

I draw a table "by hand", by using primitives of Core Graphics.
The view is re-draw when I click a button.
The problem is that when I profile the code in Instruments, the VM Regions keep increase, while Heap and Anonymous VM oscillate (and I would expect it).
And the details about CoreAnimation:
An excerpt of my drawRect:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect paperRect = CGRectMake(self.bounds.origin.x+hourLabelSize+self.marginX,
10 +self.cellBorder ,
self.bounds.size.width,
self.cellHeight * 48
);
// custom shadow
drawLinearGradient(context, paperRect, whiteColor.CGColor, lightGrayColor.CGColor);
CGContextSaveGState(context);
CGFloat outerMargin = 5.0f;
CGFloat eventSize;
// Draw table
CGRect hourRect;
CGRect outerRect;
CGMutablePathRef outerPath;
CGRect strokeRect;
CGRect rowRect;
for (int j=0; j < 7;j++)
{
for (int i=0; i < numberOfRows; i++)
{
// Odd index means we are in the half of an hour
if ( (i%2) == 0)
{
[hour setString:[NSString stringWithFormat:#"%d:00",(int)floor(i/2)]];
// Draw box around hours //
if (j == 0 && i >0)
{
CGContextSaveGState(context);
hourRect = CGRectMake(5, i*cellHeight-5, hourLabelSize, 28);
outerRect = CGRectInset(hourRect, outerMargin, outerMargin);
outerPath = newRoundedRectForRect(outerRect, 6.0);
CGContextAddPath(context, outerPath);
CFRelease(outerPath); // <--- This solve the leak!!
// Draw gradient
strokeRect = CGRectInset(hourRect, 5.0, 5.0);
CGContextSetStrokeColorWithColor(context, boxColor.CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextStrokeRect(context, strokeRect);
drawLinearGradient(context, strokeRect, whiteColor.CGColor, lightGrayColor.CGColor);
CGContextRestoreGState(context);
}
}
else
{
[hour setString:[NSString stringWithFormat:#"%d:30",(int)floor(i/2)]];
}
// Draw hours
if (j == 0 && i > 0)
[hour drawInRect:CGRectMake(0, i*cellHeight, hourLabelSize+10, 26) withFont:font lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
// Draw row
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, boxColor.CGColor);
CGContextSetLineWidth(context, self.cellBorder);
rowRect = CGRectMake(j*cellWidth + hourLabelSize +self. marginX - self.cellBorder/2, i*cellHeight+10 + self.cellBorder / 2, cellWidth , cellHeight);
CGContextStrokeRect(context, rowRect);
} //
CGContextFlush(context);
CGContextRestoreGState(context);
The first thing I do not understand is why I see VM:CoreAnimation. CoreGraphics is part of Core Animation?
Secondly, what I shall watch as syntoms of bad allocation: VM Regions, XCode measurements or Head and Anonymous?
Regards!
[EDIT]
The createRoundedRect is as follows
CGMutablePathRef newRoundedRectForRect(CGRect rect, CGFloat radius)
{
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMaxY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMaxY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMinY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMinY(rect), radius);
CGPathCloseSubpath(path);
return path;
}:
and the drawLineGradient.m :
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = #[(__bridge id) startColor, (__bridge id) endColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
colors = nil;
}
The reason you see core animation is because all iOS views are layer-backed views.
The reason for your leak is that you release the path.
ARC does not manage Core Foundation objects.

UIView With Multiple DrawRect Methods

I'm currently trying to create a Grid/Cinematic Overlay with a UIView.
I created a few methods; drawVerticalLine and Horizontal Lines and stuff...
I have a UIViewController that inits the UIGridView. I can put all my methods in the draw rect and draw them all at once.
However, I want to be able to call them individually from the ViewController. When I try to doenter code here that. I get an ": CGContextDrawPath: invalid context 0x0" Code Below.
From my ViewController I want to be able to call "drawGrid :withColor :andLines;" Or something
-
(void)drawRect:(CGRect)rect
{
if (self.verticalLinesON == YES) {
[self drawVerticalLinesForGrid:100 :[UIColor redColor] :[UIColor greenColor]];
}
[self show16NineOverLay:[UIColor orangeColor]];
[self show4ThreeOverLay:[UIColor orangeColor]];
[self drawHorizontalLinesForGrid:100 :[UIColor blueColor] :[UIColor yellowColor]];
}
-(void)drawVerticalLinesForGrid:(float)sectionsVertically :(UIColor *)lineColor1 :(UIColor *)lineColor2
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
int i = 0;
float amountOfSectionsVertically = sectionsVertically;
for (i = 1; i < amountOfSectionsVertically; i++)
{//Horizontal Lines first.
float xCoord = self.frame.size.width * ((i+0.0f)/amountOfSectionsVertically);
CGContextMoveToPoint(context, xCoord, 0);
CGContextAddLineToPoint(context, xCoord, self.frame.size.height);
if (i%2 == 1)
{//if Odd
CGContextSetStrokeColorWithColor(context, lineColor1.CGColor);
}
else if(i%2 == 0)
{//if Even
CGContextSetStrokeColorWithColor(context, lineColor2.CGColor);
}
CGContextStrokePath(context);
}
}
-(void)drawHorizontalLinesForGrid :(float)sectionsHorizontally :(UIColor *)lineColor1 :(UIColor *)lineColor2
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
int i = 0;
float amountOfSectionsHorizontally = sectionsHorizontally;
for (i = 1; i < amountOfSectionsHorizontally; i++)
{//Vertical Lines first.
float yCoord = self.frame.size.height * ((i+0.0f)/amountOfSectionsHorizontally);
CGContextMoveToPoint(context, 0, yCoord);
CGContextAddLineToPoint(context, self.frame.size.width, yCoord);
if (i%2 == 1)
{//if Odd
CGContextSetStrokeColorWithColor(context, lineColor1.CGColor);
}
else if(i%2 == 0)
{//if Even
CGContextSetStrokeColorWithColor(context, lineColor2.CGColor);
}
CGContextStrokePath(context);
}
}
-(void)show16NineOverLay:(UIColor *)lineColor
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 10);
//x/y
float yCoord = (0.5) * (self.frame.size.height * (1.778)
What you should do is set some state on your grid view class that specifies what should be drawn (just vertical, just horizontal, both, etc) and then call setNeedsDisplay on the view.
This will trigger a call to drawRect:. Then your drawRect: method should look at its current state and call just the appropriate methods to draw the desired parts.
You must never directly call drawRect: on a view.

Draw error on retina screen

I am working on "drawing" project.My project works well on screen. But when i test it on retina screen so i got an image like this retina screen!
It works on ipad2 very well.
some part of my code:
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
[curImage drawAtPoint:CGPointMake(0, 0)];
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
if(previousPoint1.x == previousPoint2.x && previousPoint1.y == previousPoint2.y && previousPoint1.x == currentPoint.x && previousPoint1.y == currentPoint.y)
{
CGContextSetLineCap(context, kCGLineCapRound);
}
else
{
CGContextSetLineCap(context, kCGLineCapRound);
}
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextSetShouldAntialias(context, YES);
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetFlatness(context, 0.1f);
CGContextSetAlpha(context, self.lineAlpha);
CGContextStrokePath(context);
- (void) calculateMinImageArea:(CGPoint)pp1 :(CGPoint)pp2 :(CGPoint)cp{
CGPoint mid1 = midPoint(pp1, pp2);
CGPoint mid2 = midPoint(cp, pp1);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(path, NULL, pp1.x, pp1.y, mid2.x, mid2.y);
CGRect bounds = CGPathGetBoundingBox(path);
CGPathRelease(path);
CGRect drawBox = bounds;
//Pad our values so the bounding box respects our line width
drawBox.origin.x -= self.lineWidth * 1;
drawBox.origin.y -= self.lineWidth * 1;
drawBox.size.width += self.lineWidth * 2;
drawBox.size.height += self.lineWidth * 2;
UIGraphicsBeginImageContext(drawBox.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
curImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setNeedsDisplayInRect:drawBox];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
CGPoint midPoint(CGPoint p1, CGPoint p2)
{
return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);}

Resources