Howard Spear Howard Spear - 4 months ago 18
iOS Question

Drawing a very thin line with CGContextAddLineToPoint and CGContextSetLineWidth

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

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));
Comments