I have this line around my shape:
The problem is, it obviously goes from 1px thick, to 2px. Here's the code I'm using:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetFillColorWithColor(context, [BUTTON_COLOR CGColor]);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(context, 1);
int radius = 8;
CGContextMoveToPoint(context, 0, self.frame.size.height / 2);
CGContextAddLineToPoint(context, POINT_WIDTH, self.frame.size.height);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius,
rect.origin.y + rect.size.height);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius,
rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius,
radius, 0.0f, -M_PI / 2, 1);
CGContextAddLineToPoint(context, POINT_WIDTH, 0);
CGContextAddLineToPoint(context, 0, self.frame.size.height / 2);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
Any ideas?
The reason your top, bottom, and right edges appear thinner than your diagonals is because you are chopping off half of the lines drawn along those edges. As Costique mentioned, Core Graphics draws a stroke so that it is centered on the path. That means half of the stroke lies on one side of the path, and half of the stroke lies on the other side. Since your path runs exactly along the top, right, and bottom edges of your view, half of the stroke falls outside the bounds of your view and isn't drawn.
Costique is also correct that you should move your paths in by .5 points. (Technically you should move by half the stroke width.) However, you're not specifying your coordinates uniformly based on the rect variable, so moving all your paths is difficult.
What you want to do is inset rect by .5 in both dimensions, adjust your graphics context so that its origin is at rect.origin, and use only rect.size to get your coordinates - not self.frame.size. Here's my test:
- (void)drawRect:(CGRect)dirtyRect
{
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextSetStrokeColorWithColor(gc, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(gc, 1);
static const CGFloat Radius = 8;
static const CGFloat PointWidth = 20;
CGRect rect = CGRectInset(self.bounds, .5, .5);
CGContextTranslateCTM(gc, rect.origin.x, rect.origin.x);
rect.origin = CGPointZero;
CGContextMoveToPoint(gc, 0, rect.size.height / 2);
CGContextAddLineToPoint(gc, PointWidth, rect.size.height);
CGContextAddLineToPoint(gc, rect.origin.x + rect.size.width - Radius,
rect.origin.y + rect.size.height);
CGContextAddArc(gc, rect.origin.x + rect.size.width - Radius,
rect.origin.y + rect.size.height - Radius, Radius, M_PI / 2, 0.0f, 1);
CGContextAddLineToPoint(gc, rect.origin.x + rect.size.width, rect.origin.y + Radius);
CGContextAddArc(gc, rect.origin.x + rect.size.width - Radius, rect.origin.y + Radius,
Radius, 0.0f, -M_PI / 2, 1);
CGContextAddLineToPoint(gc, PointWidth, 0);
CGContextAddLineToPoint(gc, 0, rect.size.height / 2);
CGContextClosePath(gc);
CGContextDrawPath(gc, kCGPathFillStroke);
} CGContextRestoreGState(gc);
}
Here's the result:
Core Graphics draws the stroke on the path edge, that is, half of the stroke width lies inside the path, and the other half, outside. Since you cannot draw outside of the view, half of the stroke is clipped by the context. You have to inset the path by half the stroke width.
The complete example:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetFillColorWithColor(context, [BUTTON_COLOR CGColor]);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(context, 1);
int radius = CORNER_RADIUS;
CGRect pathRect = CGRectInset(self.bounds, 0.5f, 0.5f);
CGContextMoveToPoint(context, CGRectGetMinX(pathRect), CGRectGetMidY(pathRect));
CGContextAddLineToPoint(context, CGRectGetMinX(pathRect) + POINT_WIDTH, CGRectGetMaxY(pathRect));
CGContextAddArc(context, CGRectGetMaxX(pathRect) - radius,
CGRectGetMaxY(pathRect) - radius, radius, M_PI / 2, 0.0f, 1);
CGContextAddArc(context, CGRectGetMaxX(pathRect) - radius, CGRectGetMinY(pathRect) + radius,
radius, 0.0f, -M_PI / 2, 1);
CGContextAddLineToPoint(context, CGRectGetMinX(pathRect) + POINT_WIDTH, CGRectGetMinY(pathRect));
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
Note that several redundant drawing commands are removed without affecting the result. Enjoy.
Related
I having the follow pop up and I want to make the edges curved.
Using this code.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
CGFloat baseWidth = 15.0;
CGFloat height = 10.0;
CGContextMoveToPoint(context, self.bounds.size.width / 2.0 - baseWidth / 2.0, height);
CGContextAddLineToPoint(context, self.bounds.size.width / 2.0, 0);
CGContextAddLineToPoint(context, self.bounds.size.width / 2.0 + baseWidth / 2.0, height);
CGContextAddLineToPoint(context, self.bounds.size.width, height);
CGContextAddLineToPoint(context, self.bounds.size.width, self.bounds.size.height);
CGContextAddLineToPoint(context, 0, self.bounds.size.height);
CGContextAddLineToPoint(context, 0, height);
CGContextClosePath(context);
CGContextStrokePath(context);
How can I curve the edges?
You are using Core Graphics and drawing those straight lines right up to each other in the screen shot above. To be able to draw curves you need to add in some calls to CGContectAddArcToPoint, there is also CGContextAddCurveToPoint but that is overkill for your implementation here.
If you want more information about Core Graphics then you can find a bunch of documentation here: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_paths/dq_paths.html#//apple_ref/doc/uid/TP30001066-CH211-TPXREF101
I'm trying to draw a rectangle that has a 15 degree angle splitting two colors down the center but the line between them is pixelated. I've tried different blend options and can't seem to figure out how to make this look clean.
This is what the header looks like now
Does anyone know how I can make the line between them look clean?
if (!_backgroundImageView.image) {
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
CGFloat tWidth = 0.26794919243f/*tan(15 degrees)*/ * height;
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef context = UIGraphicsGetCurrentContext();
if (_color1) {
CGContextSetFillColorWithColor(context, _color1.CGColor);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 1.0f + RoundPixelValue((width + tWidth) / 2.0f), 0);
CGContextAddLineToPoint(context, 1.0f + RoundPixelValue((width - tWidth) / 2.0f), height);
CGContextAddLineToPoint(context, 0, height);
CGContextFillPath(context);
}
if (_color2) {
CGContextSetFillColorWithColor(context, _color2.CGColor);
CGContextMoveToPoint(context, width, 0);
CGContextAddLineToPoint(context, RoundPixelValue((width + tWidth) / 2.0f) - 1.0f, 0);
CGContextAddLineToPoint(context, RoundPixelValue((width - tWidth) / 2.0f) - 1.0f, height);
CGContextAddLineToPoint(context, width, height);
CGContextFillPath(context);
}
_backgroundImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
The main issue here is that you're not creating the context with the correct scale.
Instead of using UIGraphicsBeginImageContext use UIGraphicsBeginImageContextWithOptions like so:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), YES, 0);
Specifying a scale of 0 sets it to the scale factor of the device screen.
This should eliminated most of the aliasing artifacts.
I am having a very weird problem with drawing a rectangle in a cell, as it is drawing the cell, however when it displays there is a discrepancy in the border color, I have searched around and haven't found anything that can shed some light on why this might be, this is a screenshot displaying the problem http://screencast.com/t/wPS507Bg9e
This is the code of how i'm drawing the rectangle
CGRect rect = CGRectMake(x, y, aRect.size.width, height);
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height );
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, ((0.0f) / 180.0 * M_PI), ((-90.0f) / 180.0 * M_PI) , 1);
CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, ((-90.0f) / 180.0 * M_PI), ((180.0f) / 180.0 * M_PI), 1);
CGContextClip(context);
Another weird thing about this problem is it seems to be only effecting the iPhone 5 and 5s, aka 4" screens only
The colour is being set once and is not manipulated after it is being set as far as i can see , any help on why this would be would be apreciated
There is no discrepancy in the color itself, but in the actual location (origin), dimensions (size), of your rectangles, or both.
From your screen shot, they are naturally anti-aliased.
This could be because:
The x, y, width & height are not all four discrete values (integers).
Line drawing falls between pixels or stretching an offscreen buffer (anti-aliasing)
Drawing in an improperly constructed retina offscreen buffer? See this post
I'd like to draw circle with hole (like donut) on mapview.
my code is here.
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CG ContextRef)context {
WPCircleOverlay * circleOverlay = self.overlay;
CGPoint centerPoint = [self pointForMapPoint:MKMapPointForCoordinate(circleOverlay.coordinate)];
CGFloat innerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.innerRadius;
CGFloat outerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.outerRadius;
CGMutablePathRef path = CGPathCreateMutable();
//CGPathMoveToPoint(path, ...);
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, outerRadius, 0, 2 * M_PI, true);
CGPathCloseSubpath(path);
// Add the inner arc to the path (later used to substract the inner area)
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, innerRadius, 0, 2 * M_PI, true);
CGPathCloseSubpath(path);
// Add the path to the context
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
CGContextEOFillPath(context);
CGPathRelease(path);
It works well in simulator, but on device it doesn't.
On device, outer circle is filled with color, and inner circle wasn't clipped.
How can I modify my code to work well on device?
I fixed it by using CGContextAddEllipseInRect method instead of CGContextAddArc.
WPCircleOverlay * circleOverlay = self.overlay;
CGRect rectForMapRect = [self rectForMapRect:mapRect];
CGPoint centerPoint = [self pointForMapPoint:MKMapPointForCoordinate(circleOverlay.coordinate)];
CGFloat innerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.innerRadius;
CGFloat outerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.outerRadius;
CGRect innerRect = CGRectMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius, innerRadius * 2.0, innerRadius * 2.0);
CGRect outerRect = CGRectMake(centerPoint.x - outerRadius, centerPoint.y - outerRadius, outerRadius * 2.0, outerRadius * 2.0);
if (CGRectIntersectsRect(rectForMapRect, outerRect)) {
CGContextAddRect(context, rectForMapRect);
CGContextSaveGState(context);
CGContextClip(context);
CGContextAddEllipseInRect(context, outerRect);
CGContextAddEllipseInRect(context, innerRect);
CGContextSaveGState(context);
CGContextEOClip(context);
UIColor * color = [self.fillColor copy];
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, outerRect);
CGContextRestoreGState(context);
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
With core graphics, is it possible to stroke the inside of a path? As opposed to the line weight being drawn half on the outside and half on the inside of a stroked path?
The reason being it would be easier to control the visible thickness of the stroke if say part of a view is on the screen edge and part is not. The part on the screen edge gets cut off, while the view edge that is fully on screen looks thicker (as both sides if the stroke are visible).
Clip to the path before you stroke it.
This draws no stroke:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextSetLineWidth(context, 14);
CGRect rrect = CGRectMake(CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect));
CGFloat radius = 30;
CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawPath(context, kCGPathStroke);
}
EDIT: This is what worked (according to the correct answer):
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 14);
CGRect pathRect = CGRectMake(10, 10, rect.size.width -20, rect.size.height -20);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:pathRect cornerRadius:20].CGPath;
CGContextAddPath(context, path);
CGContextClip(context);
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathEOFillStroke);
}