Related
For an iPhone application I want to draw a circle, that is only for an x percentage filled.
Something like this:
I have no problems calculating the radius, the degrees or the radians, that is no problem. Also drawing the circle is already done. But how do I get the iPhone SDK to draw the part that is filled.
I can draw a rectangle that size, but not part of a circle.
I just want to draw that on a a normal context.
Hope someone can give me any pointers here.
A lot of people have showed you how this can be done in Core Graphics but it can also be done with Core Animation which gives the big addition of easily being able to animate the percentage of the pie shape.
The following code will create both the ring and the partly filled layers (even though you said that you already can draw the ring) since its nice to have both the ring and the pie shape to be drawn using the same method.
If you animate the strokeStart or strokeEnd properties of the pieShape layer you will have the percentage animate. As with all Core Animation code you will need to add QuartzCore.framework to your project and include <QuartzCore/QuartzCore.h> in your code.
// Create a white ring that fills the entire frame and is 2 points wide.
// Its frame is inset 1 point to fit for the 2 point stroke width
CGFloat radius = MIN(self.frame.size.width,self.frame.size.height)/2;
CGFloat inset = 1;
CAShapeLayer *ring = [CAShapeLayer layer];
ring.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)
cornerRadius:radius-inset].CGPath;
ring.fillColor = [UIColor clearColor].CGColor;
ring.strokeColor = [UIColor whiteColor].CGColor;
ring.lineWidth = 2;
// Create a white pie-chart-like shape inside the white ring (above).
// The outside of the shape should be inside the ring, therefore the
// frame needs to be inset radius/2 (for its outside to be on
// the outside of the ring) + 2 (to be 2 points in).
CAShapeLayer *pieShape = [CAShapeLayer layer];
inset = radius/2 + 2; // The inset is updated here
pieShape.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)
cornerRadius:radius-inset].CGPath;
pieShape.fillColor = [UIColor clearColor].CGColor;
pieShape.strokeColor = [UIColor whiteColor].CGColor;
pieShape.lineWidth = (radius-inset)*2;
// Add sublayers
// NOTE: the following code is used in a UIView subclass (thus self is a view)
// If you instead chose to use this code in a view controller you should instead
// use self.view.layer to access the view of your view controller.
[self.layer addSublayer:ring];
[self.layer addSublayer:pieShape];
Use CGContext's arc functions:
CGContextAddArc(context,
centerX,
centerY,
radius,
startAngleRadians,
endAngleRadians,
clockwise ? 1 : 0);
See the documentation for CGContextAddArc().
Try this:
CGContextMoveToPoint(the center point)
CGContextAddLineToPoint(the starting point of the fill path on the circumference)
CGContextAddArcToPoint(the ending point of the fill path on the circumference)
CGContextAddLineToPoint(the center point)
CGContextFillPath
I implemented a pie progress view that looks similar to what you are doing. It's open source. Hopefully the source code will help.
SSPieProgressView.h source
SSPieProgressView.m source
CircleViewController.h
#import <UIKit/UIKit.h>
#interface CircleViewController : UIViewController
#end
CircleViewController.m
#import "CircleViewController.h"
#import "GraphView.h"
#interface CircleViewController ()
#end
#implementation CircleViewController
- (void)viewDidLoad {
[super viewDidLoad];
GraphView *graphView = [[GraphView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
graphView.backgroundColor = [UIColor whiteColor];
graphView.layer.borderColor = [UIColor redColor].CGColor;
graphView.layer.borderWidth = 1.0f;
[self.view addSubview:graphView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
GraphView.h
#import <UIKit/UIKit.h>
#interface GraphView : UIView
#end
GraphView.m
#import "GraphView.h"
#implementation GraphView
- (void)drawRect:(CGRect)rect {
CGPoint circleCenter = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
[self drawCircleWithCircleCenter:(CGPoint) circleCenter radius:80 firstColor:[UIColor blueColor].CGColor secondeColor:[UIColor redColor].CGColor lineWidth:2 startDegree:0 currentDegree:90];
//[self drawCircleWithCircleCenter2:(CGPoint) circleCenter radius:80 firstColor:[UIColor blueColor].CGColor secondeColor:[UIColor redColor].CGColor lineWidth:2 startDegree:0 currentDegree:90];
}
- (void)drawCircleWithCircleCenter:(CGPoint) circleCenter
radius:(CGFloat)radius
firstColor:(CGColorRef)firstColor
secondeColor:(CGColorRef)secondeColor
lineWidth:(CGFloat)lineWidth
startDegree:(float)startDegree
currentDegree:(float)endDegree {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, lineWidth);
CGContextMoveToPoint(context, circleCenter.x, circleCenter.y);
CGContextAddArc(context, circleCenter.x , circleCenter.y, radius, [self radians:startDegree], [self radians:endDegree], 0);
CGContextSetFillColorWithColor(context, firstColor);
CGContextFillPath(context);
CGContextMoveToPoint(context, circleCenter.x, circleCenter.y);
CGContextAddArc(context, circleCenter.x, circleCenter.y, radius, [self radians:endDegree], [self radians:startDegree], 0);
CGContextSetFillColorWithColor(context, secondeColor);
CGContextFillPath(context);
}
- (void)drawCircleWithCircleCenter2:(CGPoint) circleCenter
radius:(CGFloat)radius
firstColor:(CGColorRef)firstColor
secondeColor:(CGColorRef)secondeColor
lineWidth:(CGFloat)lineWidth
startDegree:(float)startDegree
currentDegree:(float)endDegree {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, lineWidth);
CGContextMoveToPoint(context, circleCenter.x, circleCenter.y);
CGContextAddArc(context, circleCenter.x , circleCenter.y, radius, [self radians:startDegree], [self radians:endDegree], 0);
CGContextSetFillColorWithColor(context, firstColor);
CGContextFillPath(context);
CGContextMoveToPoint(context, circleCenter.x, circleCenter.y);
CGContextAddArc(context, circleCenter.x, circleCenter.y, radius, [self radians:endDegree], [self radians:startDegree], 0);
CGContextSetStrokeColorWithColor(context, secondeColor);
CGContextStrokePath(context);
}
-(float) radians:(double) degrees {
return degrees * M_PI / 180;
}
#end
note: you can use one of the 2 methods:
"drawCircleWithCircleCenter" or "drawCircleWithCircleCenter2"
this code if you want to split cell on 2 parts only
if you want to split cell on more than 2 parts you can check this : "Drawing a circle ,filled different parts with different color" and check the answer start with this Phrase "we have 6 class"
Well, since nobody used NSBezierPath so far, I figured I could provide the solution I recently used for the same problem:
-(void)drawRect:(NSRect)dirtyRect
{
double start = -10.0; //degrees
double end = 190.0; //degrees
NSPoint center = NSMakePoint(350, 200);
double radius = 50;
NSBezierPath *sector = [NSBezierPath bezierPath];
[sector moveToPoint:center];
[sector appendBezierPathWithArcWithCenter:center radius:radius startAngle:start endAngle:end];
[sector lineToPoint:center];
[sector fill];
}
Below is a full method I am using that does this with Core Graphics, adapting and expanding on mharper's comment above.
This code is for OSX Cocoa, but could easily be changed to iOS, by modifying how you get the context.
- (void)drawPieShapedCircleWithRadius:(CGFloat)radius
strokeColor:(CGColorRef)strokeColor
fillColor:(CGColorRef)fillColor
lineWidth:(CGFloat)lineWidth
currentDegrees:(float)currentDegrees
startDegrees:(float)startDegrees {
// get the context
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
// Set the color of the circle stroke and fill
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetFillColorWithColor(context, fillColor);
// Set the line width of the circle
CGContextSetLineWidth(context, 1);
// Calculate the middle of the circle
CGPoint circleCenter = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
// Move the bezier to the center of the circle
CGContextMoveToPoint(context, circleCenter.x, circleCenter.y); // move to the center point
// Draw the arc from the start point (hardcoded as the bottom of the circle) to the center
CGContextAddLineToPoint(context, circleCenter.x, circleCenter.y + radius);
// Draw the arc around the circle from the start degrees point to the current degrees point
CGContextAddArc(context, circleCenter.x , circleCenter.y, radius, [self radians:startDegrees], [self radians:startDegrees + currentDegrees], 0);
// Draw the line back into the center of the circle
CGContextAddLineToPoint(context, circleCenter.x, circleCenter.y);
// Fill the circle
CGContextFillPath(context);
// Draw the line around the circle
CGContextStrokePath(context);
}
Try this code in a UIView, Example "MyChartClass"...
- (void)drawRect:(CGRect)rect {
int c=(int)[itemArray count];
CGFloat angleArray[c];
CGFloat offset;
int sum=0;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetAllowsAntialiasing(context, false);
CGContextSetShouldAntialias(context, false);
for(int i=0;i<[itemArray count];i++) {
sum+=[[itemArray objectAtIndex:i] intValue];
}
for(int i=0;i<[itemArray count];i++) {
angleArray[i]=(float)(([[itemArray objectAtIndex:i] intValue])/(float)sum)*(2*3.14);
CGContextMoveToPoint(context, radius, radius);
if(i==0)
CGContextAddArc(context, radius, radius, radius, 0,angleArray[i], 0);
else
CGContextAddArc(context, radius, radius, radius,offset,offset+angleArray[i], 0);
offset+=angleArray[i];
CGContextSetFillColorWithColor(context, ((UIColor *)[myColorArray objectAtIndex:i]).CGColor);
CGContextClosePath(context);
CGContextFillPath(context);
}
}
Implementation in your UIViewController
MyChartClass *myChartClass=[[MyChartClass alloc]initWithFrame:CGRectMake(0, 0, 200, 200)];
myChartClass.backgroundColor = [UIColor clearColor];
myChartClass.itemArray=[[NSArray alloc]initWithObjects:#"75",#"25", nil];
myChartClass.myColorArray=[[NSArray alloc]initWithObjects:[UIColor blackColor],[UIColor whiteColor], nil];
myChartClass.radius=100;
[self.view addSubview:myChartClass];
Regards.
I am trying to draw an annotation for map , my view is a subclass of MKAnnotationView
I need a shape something like shown below
What I am getting is like this :
Here is the code which I am using :
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx= UIGraphicsGetCurrentContext();
UIGraphicsPushContext(ctx);
CGRect bounds = [self bounds];
CGPoint topLeft = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPoint topRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPoint midBottom = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGFloat height = bounds.size.height;
CGFloat width = bounds.size.width;
//draw semi circle
CGContextBeginPath(ctx);
CGContextAddArc(ctx, width/2, height/2, width/2, 0 ,M_PI, YES);
//draw bottom cone
CGContextAddLineToPoint(ctx, midBottom.x, midBottom.y);
CGContextAddLineToPoint(ctx, topRight.x, topRight.y + height/2); // mid right
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextFillPath(ctx);
UIGraphicsPopContext();
}
You can achieve the desired effect if you replace your lines with quad curves:
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx= UIGraphicsGetCurrentContext();
UIGraphicsPushContext(ctx);
CGRect bounds = [self bounds];
CGPoint topLeft = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPoint topRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPoint midBottom = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGFloat height = bounds.size.height;
CGFloat width = bounds.size.width;
//draw semi circle
CGContextBeginPath(ctx);
CGContextAddArc(ctx, width/2, height/2, width/2, 0 ,M_PI, YES);
//draw bottom cone
CGContextAddQuadCurveToPoint(ctx, topLeft.x, height * 2 / 3, midBottom.x, midBottom.y);
CGContextAddQuadCurveToPoint(ctx, topRight.x, height * 2 / 3, topRight.x, topRight.y + height/2);
// CGContextAddLineToPoint(ctx, midBottom.x, midBottom.y);
// CGContextAddLineToPoint(ctx, topRight.x, topRight.y + height/2); // mid right
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextFillPath(ctx);
UIGraphicsPopContext();
}
This employs a quadratic bezier curve, which is a good curve when you don't want inflection points. You can achieve a similar curve with the cubic bezier, but if you're not careful with your control points, you can get undesired inflection points. The quadratic curve is just a little easier with only one control point per curve.
By choosing control points with x values the same as the start and end of the semicircle, it ensures a smooth transition from the circle to the curve leading down to the point. By choosing y values for those control points which are relatively close to the start and end of the semicircle, it ensures that the curve will transition quickly from the semicircle to the point. You can adjust these control points, but hopefully this illustrates the idea.
For illustration of the difference between these two types of curves, see the Curves section of the Paths chapter of the Quartz 2D Programming Guide.
I'm having trouble drawing atop an image in a UIImageView, I've already looked at "Draw another image on a UIImage" But it was only so much help. Here's my scenario: I have a UIPopoverController with a UITableView in it and I want to display a triangle pointing up when the user is at the bottom of scrollable table view.
My code:
- (UIImage *) drawTriangleInViewForSize:(CGSize)sizeOfView
imageToDrawOn:(UIImage *)underImage
isAtTop:(BOOL)top{
CGPoint firstPoint;
CGPoint secondPoint;
CGPoint thirdPoint;
if(!top){
//I want to draw a equilateral triangle (side length 10) in the center of the image in
//the imageView, these are the coordinates I am using.
firstPoint = CGPointMake(underImage.size.width * 0.5 + 5, underImage.size.height * 0.5 - 5);
secondPoint = CGPointMake((firstPoint.x - 10), firstPoint.y);
thirdPoint = CGPointMake(underImage.size.width * 0.5,
underImage.size.width * 0.5 + 5);
}
*/
**DISREGARD**
else{
firstPoint = CGPointMake(sizeOfView.width * 0.5 + 5,
self.tableViewChoices.rowHeight * 0.5 - 5);
secondPoint = CGPointMake((firstPoint.x - 10), firstPoint.y);
thirdPoint = CGPointMake(sizeOfView.width * 0.5,
self.tableViewChoices.rowHeight * 0.5 + 5);
}*/
//get the size of the image for the drawInRect: method
CGFloat imageViewWidth = sizeOfView.width;
CGFloat imageViewHeight = self.tableViewChoices.rowHeight;
//set the graphics context to be the size of the image
UIGraphicsBeginImageContextWithOptions(underImage.size, YES, 0.0);
[underImage drawInRect:CGRectMake(0.0, 0.0, imageViewWidth, imageViewHeight)];
//set the line attributes
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
CGContextSetLineWidth(ctx, 0.05);
UIGraphicsPushContext(ctx);
//draw the triangle
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, firstPoint.x, firstPoint.y);
CGContextAddLineToPoint(ctx, secondPoint.x, secondPoint.y);
CGContextAddLineToPoint(ctx, thirdPoint.x, thirdPoint.y);
CGContextAddLineToPoint(ctx, firstPoint.x, firstPoint.y);
CGContextClosePath(ctx);
UIGraphicsPopContext();
//get the image
UIImage *rslt = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return rslt;
}
I can't quite seem to draw to the triangle to the UIImage in the imageView, the end result is either a completely black ImageView (the UIImage is just a white.png), or one line at the top of the imageview (depending on whether I use drawInRect: or drawAtPoint: and what coordinates I use). All I need to do is draw a triangle pointing up, not a hard thing, and it really looks like I'm doing it "by the book."
Here is my completed code to draw a triangle onto a UIImage in a UIImageView.
//draws triangle up or down on a UIImage.
+(UIImage *) drawTriangleInViewForSize:(CGSize)sizeOfView
imageToDrawOn:(UIImage *)underImage
isAtTop:(BOOL)top
{
CGFloat rowHeight = underImage.size.height; //44; //self.tableViewChoices.rowHeight;
CGPoint firstPoint;
CGPoint secondPoint;
CGPoint thirdPoint;
CGFloat imageViewWidth = sizeOfView.width;
CGFloat imageViewHeight = rowHeight;
if(!top){
//draw a upward facing triangle in the center of the view.
firstPoint = CGPointMake(imageViewWidth * 0.5 + 15, imageViewHeight * 0.5 - 5);
secondPoint = CGPointMake((firstPoint.x - 30), firstPoint.y);
thirdPoint = CGPointMake(imageViewWidth * 0.5,
imageViewHeight * 0.5 + 5);
}else{
//disregard this 'else'
firstPoint = CGPointMake(sizeOfView.width * 0.5 + 15,
rowHeight * 0.5 - 5);
secondPoint = CGPointMake((firstPoint.x - 10), firstPoint.y);
thirdPoint = CGPointMake(sizeOfView.width * 0.5,
rowHeight * 0.5 + 5);
}
//get the image context with options(recommended funct to use)
//get the size of the imageView
UIGraphicsBeginImageContextWithOptions(CGSizeMake(imageViewWidth, imageViewHeight), YES, 0.0);
//use the the image that is going to be drawn on as the receiver
[underImage drawInRect:CGRectMake(0.0, 0.0, imageViewWidth, imageViewHeight)];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 0.5);
//UIGraphicsPushContext(ctx);
//uses path ref
CGMutablePathRef path = CGPathCreateMutable();
//draw the triangle
CGPathMoveToPoint(path, NULL, firstPoint.x, firstPoint.y);
CGPathAddLineToPoint(path, NULL, secondPoint.x, secondPoint.y);
CGPathAddLineToPoint(path, NULL, thirdPoint.x, thirdPoint.y);
CGPathAddLineToPoint(path, NULL, firstPoint.x, firstPoint.y);
//close the path
CGPathCloseSubpath(path);
//add the path to the context
CGContextAddPath(ctx, path);
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
CGContextFillPath(ctx);
CGContextAddPath(ctx, path);
CGContextStrokePath(ctx);
CGPathRelease(path);
//UIGraphicsPopContext();
//get the new image with the triangle
UIImage *rslt = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return rslt;
}
Your code looks too complicated for that kind of task. Why don't you use simple UIImageView with any triangle image you ever want? Just show it when the table at the bottom and that's all.
Note, that when you're implementing delegate for UITableView you're also able to catch it's UIScrollView's delegate messages such as scrollView:didEndDecelerating: and so on.
Regarding your code you've missed an actual path draw:
CGContextAddPath(context, trianglePath);
CGContextFillPath(context);
To make it possible you should use path-related functions. Using CGContext-related functions seems to be improper since such approach may remove old path. So the better way is to create your own mutable path in the context, draw your triangle in it and then draw this path into context by functions mentioned above.
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:
I am using mapkit on iPhone with iOS 4.
I am using a custom overlay and a custom overlay view, to draw shapes on the map.
At the moment, shapes are just rectangles, but I am planning something more sophisticated. This is why I am not using the MKPolygon overlay type.
This is the code for my overlay view drawing method:
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
// Clip context to bounding rectangle
MKMapRect boundingMapRect = [[self overlay] boundingMapRect];
CGRect boundingRect = [self rectForMapRect:boundingMapRect];
CGContextAddRect(context, boundingRect);
CGContextClip(context);
// Define shape
CGRect shapeRect = CGRectMake(0.5f, 0.5f, boundingRect.size.width - 1.0f, boundingRect.size.height - 1.0f);
// Fill
CGContextSetRGBFillColor(context, 0.5f, 0.5f, 0.5f, 0.5f);
CGContextFillRect(context, shapeRect);
// Stroke
CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.75f);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeRect(context, shapeRect);
}
The problem is that rectangles get correctly filled (so it appears their bounding rect is correctly set), but they don't get stroked.
Can anyone help?
Thanks!
As reported in some previous comments, the problem is with line width. More generally, all drawing is automatically scaled to follow the map zooming, so if you want some of your drawing metrics to be zoom-independent, you have to divide it by zoomScale.
Here is the new code, that works correctly on my iPhone 4:
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
// Clip context to bounding rectangle
MKMapRect boundingMapRect = [[self overlay] boundingMapRect];
CGRect boundingRect = [self rectForMapRect:boundingMapRect];
CGContextAddRect(context, boundingRect);
CGContextClip(context);
// Define shape
CGRect shapeRect = CGRectInset(boundingRect, 2.0f / zoomScale, 2.0f / zoomScale);
// Fill
CGContextSetRGBFillColor(context, 0.5f, 0.5f, 0.5f, 0.5f);
CGContextFillRect(context, shapeRect);
// Stroke
CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.75f);
CGContextSetLineWidth(context, 4.0f / zoomScale);
CGContextStrokeRect(context, shapeRect);
}
I will also report the code I am using in the overlay to calculate and return the bounding rectangle, because I think it can help:
-(MKMapRect)boundingMapRect
{
// Overlay bounds
CLLocationCoordinate2D topLeftcoordinate = <the top-left coordinate of overlay>;
CLLocationCoordinate2D bottomRightCoordinate = <the bottom-right coordinate of overlay>;
// Convert them to map points
MKMapPoint topLeftPoint = MKMapPointForCoordinate(topLeftcoordinate);
MKMapPoint bottomRightPoint = MKMapPointForCoordinate(bottomRightCoordinate);
// Calculate map rect
return MKMapRectMake(topLeftPoint.x, topLeftPoint.y, bottomRightPoint.x - topLeftPoint.x, topLeftPoint.y - bottomRightPoint.y);
}
Thank you all for your comments and suggestions.
Currently, we should use MKOverlayRenderer instead of MKOverlayView.
In method -(void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context,
use scaleFactor for configuring the stroke width of lines or other
attributes that might be affected by the scale of the map’s
contents.
Refer to Apple official site drawMapRect:zoomScale:inContext: of MKOverlayRenderer.