Drawing a very thin line with CGContextAddLineToPoint and CGContextSetLineWidth

Howard Spear picture Howard Spear · Oct 14, 2012 · Viewed 14.6k times · Source

I want to draw a very thin hairline width of a line in my UIView's drawRect method. The line I see that has a value 0.5 for CGContextSetLineWidth doesn't match the same 1.0 width value that is used to draw a border CALayer.

You can see the difference between the two - the red line (width = 1) is a lot thinner than the purple/blue line (width = 0.5).

Border drawn with CALayer

Here's how I am drawing my pseudo 1.0 width horizontal line:

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextSetLineWidth(ctx, 0.5); // I expected a very thin line

CGContextMoveToPoint(ctx, 0, y);
CGContextAddLineToPoint(ctx, self.bounds.size.width, y);

CGContextStrokePath(ctx);

Here's a border for the same view, this time using 1.0 border width:

UIView *myView = (UIView *)self;
CALayer *layer = myView.layer;
layer.borderColor = [UIColor redColor].CGColor;
layer.borderWidth = 1.0;

What do I need to do differently to draw my own custom line that's the same width as the CALayer version?

Answer

rob mayoff picture rob mayoff · Oct 14, 2012

When you stroke a path, the stroke straddles the path. To say it another way, the path lies along the center of the stroke.

If the path runs along the edge between two pixels, then the stroke will (partially) cover the pixels on both sides of that edge. With a line width of 0.5, a horizontal stroke will extend 0.25 points into the pixel above the path, and 0.25 points into the pixel below the path.

You need to move your path so it doesn't run along the edge of the pixels:

CGFloat lineWidth = 0.5f;
CGContextSetLineWidth(ctx, lineWidth);

// Move the path down by half of the line width so it doesn't straddle pixels.
CGContextMoveToPoint(ctx, 0, y + lineWidth * 0.5f);
CGContextAddLineToPoint(ctx, self.bounds.size.width, y + lineWidth * 0.5f);

But since you're just drawing a horizontal line, it's simpler to use CGContextFillRect:

CGContextSetFillColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextFillRect(ctx, CGRectMake(0, y, self.bounds.size.width, 0.5f));