2

I have this line around my shape:

enter image description here

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?

Andrew
  • 15,935
  • 28
  • 121
  • 203

2 Answers2

7

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:

enter image description here

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
3

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.

Costique
  • 23,712
  • 4
  • 76
  • 79
  • It's best to manually move all your path points 0.5 pt "inside". Just adjust the points' coordinates. – Costique Feb 21 '12 at 18:13
  • The lines on the left are still thicker than the other lines. They appear to be 0.5 too thick. Any ideas how to get rid of that? – Andrew Feb 21 '12 at 18:45
  • @Andrew That's definitely antialiasing which makes all curves and sloped lines appear thicker than horizontal and vertical lines. To be honest, I don't know how to deal with _that_ except make the stroke significantly wider. – Costique Feb 21 '12 at 18:55