iOS draw graph with gradient on mapkit - ios

I try to modify the Breadcrumb example from Apple in the way that the drawn path has a gradient. I can not make I work. The colour of the path is black and not gradient.
Someone got an idea?
if (path != nil)
{
CGContextAddPath(context, path);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextReplacePathWithStrokedPath(context);
path = CGContextCopyPath(context);
CGContextAddPath(context, path);
CGContextSaveGState(context); {
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(rgb, (__bridge CFArrayRef)#[
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor redColor].CGColor
], (CGFloat[]){ 0.0f, 1.0f });
CGColorSpaceRelease(rgb);
CGPoint startPoint = CGPointMake(MKMapRectGetMidX(clipRect), MKMapRectGetMinY(clipRect));
CGPoint endPoint = CGPointMake(MKMapRectGetMidX(clipRect), MKMapRectGetMaxY(clipRect));
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
} CGContextRestoreGState(context);
CGContextAddPath(context, path);
CGPathRelease(path);
CGContextDrawPath(context, kCGPathFillStroke);
}

Related

IOS app different when loading via TestFlight

I have a view in storyboard controller which extends my custom UIView and when I try to draw a gradient in drawRect it displays fine when loading on to device/sim via XCode but when I install via TestFlight after uploading to ITunes Connect the gradient isn't present at first (displays black background) - it needs a rotation of the device and the gradient displays fine again. What could be causing this behaviour?
- (void)drawRect:(CGRect)rect {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
NSArray *gradientColors = [NSArray arrayWithObjects:
(id)[[self colorWithHexString:#"FF7542"]CGColor],
(id)[[self colorWithHexString:#"FF7542"]CGColor],
(id)[[self colorWithHexString:#"FFC0A1"]CGColor],
nil];
CGFloat gradientLocations[] = {0, 1};
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) gradientColors, gradientLocations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
NSShadow *shadow = [[NSShadow alloc] init];
[shadow setShadowColor : BLACK_SHADOW];
[shadow setShadowOffset : CGSizeMake (1.0, 1.0)];
}
I solved this by creating my gradient another way and strangely it shows first time when loaded without the need for a rotation.
CGContextRef current_context = UIGraphicsGetCurrentContext();
CGContextSaveGState(current_context);
// Gradient
CGFloat locations[2] = {0.0, 1.0};
CGFloat components[8] = {1.00,0.75,0.63,1.0,1.00,0.46,0.26,1.0};
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, 2);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextDrawLinearGradient(current_context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorspace);

iOS drawRect draw a border

Hi I have draw a gradient in core graphic using drawRect function..
but I don't know how to draw a border to surround this view?
this is my code, could anyone help?
- (void)drawRect:(CGRect)rect {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
NSArray *gradientColors = [NSArray arrayWithObjects:(id)[UIColor blackColor].CGColor, [UIColor colorWithRed:90/255.0 green:0 blue:0 alpha:1].CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) gradientColors, NULL);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
If you just want to draw a border around the view, try this:
UIBezierPath *border = [UIBezierPath bezierPathWithRect:rect];
[[UIColor redColor] setStroke];
[border setLineWidth:4.0];
[border stroke];
Use it at the end of drawRect: method.

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.

Draw CAGradient within MKPolyLineView

i have just a problem with my MKPolyLineView. I simply try to make a color gradient to the Polyline, but with CAGradient it doenst work. I subclasses MKPolylineView and redrawing in
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
UIColor *darker = [UIColor blackColor];
CGFloat baseWidth = self.lineWidth / zoomScale;
// draw the dark colour thicker
CGContextAddPath(context, self.path);
CGContextSetStrokeColorWithColor(context, darker.CGColor);
CGContextSetLineWidth(context, baseWidth * 1.5);
CGContextSetLineCap(context, self.lineCap);
CGContextStrokePath(context);
// now draw the stroke color with the regular width
CGContextAddPath(context, self.path);
CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
CGContextSetLineWidth(context, baseWidth);
CGContextSetLineCap(context, self.lineCap);
CGContextStrokePath(context);
[super drawMapRect:mapRect zoomScale:zoomScale inContext:context];
}
but even that is not working (StrokeColor = red). Any ideas how to get a gradient into
the Polyline? (Highcolor, centercolor, lowcolor)
Thanks everyone.
To paint a MKPolyline with a gradient, you can use a custom subclass of MKPolylineView. As CoreGraphics does not support stroking a path with a gradient, we have to
convert the path to a shape that traces the paths edge using CGPathCreateCopyByStrokingPath
clip the context to that shape
fill using CGContextDrawLinearGradient
Here is a subclass to get you started:
#interface TWOGradientPolylineView : MKPolylineView
#end
#implementation TWOGradientPolylineView
- (void)strokePath:(CGPathRef)path inContext:(CGContextRef)context
{
CGFloat lineWidth = CGContextConvertSizeToUserSpace(context, (CGSize){self.lineWidth, self.lineWidth}).width;
CGPathRef pathToFill = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, self.lineCap, self.lineJoin, self.miterLimit);
CGRect rect = CGPathGetBoundingBox(pathToFill);
CGContextAddPath(context, pathToFill);
CGPathRelease(pathToFill);
CGContextClip(context);
CGFloat gradientLocations[2] = {0.0f, 1.0f};
CGFloat gradientColors[8] = {1.0f, 0.0f, 0.0f, 0.75f, 1.0f, 1.0f, 0.0f, 0.75f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocations, 2);
CGColorSpaceRelease(colorSpace);
CGPoint gradientStart = rect.origin;
CGPoint gradientEnd = {CGRectGetMaxX(rect), CGRectGetMaxY(rect)};
CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
}
#end
Here is a screenshot of a path drawn with the class above:

Custom map path line

I'm having trouble with drawing a line on the map with a stroke. (inside color and outside color) I beleive i'm on the right path and have subclassed mkOverlayView to override the drawing (needs to fill with the road size) so inside drawMapRect...
CGFloat lineWidth = MKRoadWidthAtZoomScale(zoomScale);
MKMapRect clipRect = MKMapRectInset(mapRect, -lineWidth, -lineWidth);
...
CGContextAddPath(context, path);
CGContextSetStrokeColorWithColor(context, line.color.CGColor);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextSetAlpha(context, 0.4f);
CGContextStrokePath(context);
CGPathRelease(path);
I'm not sure how to add the stroke. Any help would be greatly appreciated. xcode 4.6 / ios 6.0+
stroke first a path ( road with) with color 1, then change stroke width and color to a thinner line with road with * 0.6 in color 2, and stroke again.
Ok, I managed to figure it out based on Mathew's suggestion but with a few tweaks...
Essentially I created a stroked path using CGPathCreateCopyByStrokingPath and made the stroke slightly darker than the base color. Then created a transparentLayer with the path and stroke, used the blend mode kCGBlendModeDestinationAtop and drew another path with only a fill this time on top. I'm not sure if this is the best approach but appears to work well.
CGPathRef newpath = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 0.0);
CGContextAddPath(context, newpath);
CGContextSetStrokeColorWithColor(context, line.color.CGColor);
CGContextSetFillColorWithColor(context, line.color.CGColor);
CGContextSetLineWidth(context, lineWidth * 0.3);
CGContextSetAlpha(context, 0.8f);
CGContextBeginTransparencyLayer (context, NULL);
CGContextStrokePath(context);
CGContextBeginPath(context);
CGContextAddPath(context, newpath);
CGContextFillPath(context);
CGContextEndTransparencyLayer(context);
CGContextSetBlendMode(context, kCGBlendModeDestinationAtop);
CGContextSetAlpha(context, 0.3f);
CGContextBeginPath(context);
CGContextAddPath(context, newpath);
CGContextFillPath(context);
CGPathRelease(path);
CGPathRelease(newpath);
EDIT Here is a simpler solution based on AlexWien's approach
if (path != nil)
{
CGPathRef path2 = CGPathCreateCopy(path);
CGFloat hue;
CGFloat saturation;
CGFloat brightness;
CGFloat alpha;
[line.color getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];
UIColor *c2 =[UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:0.4];
CGContextAddPath(context, path);
CGContextSetStrokeColorWithColor(context, c2.CGColor);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextStrokePath(context);
CGPathRelease(path);
CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
CGContextAddPath(context, path2);
CGContextSetRGBStrokeColor(context, 1.0f, 1.0f, 1.0f, 0.3f);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth/2.0f);
CGContextStrokePath(context);
CGPathRelease(path2);
}

Resources