Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 285
Howard Spear Avatar asked Oct 14 '12 02:10

Howard Spear


2 Answers

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));
like image 136
rob mayoff Avatar answered Sep 23 '22 07:09

rob mayoff


You need to turn off antialiasing to get a thin line when not drawn on an integral.

CGContextSetShouldAntialias(ctx, NO)
like image 16
Conor Avatar answered Sep 26 '22 07:09

Conor