Core Graphics is not drawing a line at the correct

2019-03-14 05:12发布

问题:

I'm trying to combine a border on an UIView with a line drawn in drawRect. I use for both the same width. But the problem is that sometimes the resulting width of the lines drawn is the same for both and sometimes not - this even changes with device orientation! But even without changing the device orientation it's in general still not the same width.

The border is drawn with:

view.layer.borderColor = [UIColor blackColor].CGColor;
view.layer.borderWidth = 1.0f;

Above that view is another view, which is a subclass of UIView. Both views have the same width and this second view covers the above view's top. The covering view uses the following code in drawRect to draw a line on the left, which is supposed to align perfectly with the border of the above view below it:

CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat x = self.bounds.origin.x;
CGFloat y = self.bounds.origin.y;
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;

CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 1.0f);

// Left line
CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x, height);
CGContextStrokePath(context);

// Right line
CGContextMoveToPoint(context, width - 1.0f, y);
CGContextAddLineToPoint(context, width - 1.0f, height);
CGContextStrokePath(context);

Any ideas how this can be achieved? Right now, even though the width is always 1.0f, it's just coincidence if both lines are drawn with the same visible width.

The goal is to end up with something that looks like a view with rounded corners on the bottom but usual edges on the top. That's why I'm doing this. The first view has the rounded corners and the second view above it matches it in width but covers the top part, so the rounded cornes on the top are not visible.

Screenshot in portrait:

Screenshot in landscape:

The yellow view is above the white view. The white view's black lines are drawn as a result of view.layer.borderWidth = 1.0f. The black lines to the left and right of the yellow view are drawn in drawRect with the code above. Orientation changes trigger a redraw but the drawing code itself remains the same. Both screenshots are from an iPhone with retina display (iPhone 4S).

As far as I can tell all values are "integers":

x: 0.000000, y: 0.000000
width: 264.000000, height: 10.000000
frame.origin.x: 33.000000, frame.origin.y: 38.000000

回答1:

I'm guessing the line you are drawing is not pixel aligned. If I'm wrong then ignore my tangent here. But it's useful information nonetheless.

I'm going to borrow some screenshots from this WWDC 2011 video:

"1-29 Session 129 - Practical Drawing for iOS Developers"

I suggest you watch the whole thing. The relevent section starts around the 20:50 mark.

Basically, imagine you want to draw a 1pt line from (1,2) to (4,2)

Core graphics will try and center it along the y value which is 2.0. This means it will try and draw the line with its top edge at y=1.5 and the bottom edge at y=2.5:

On a retina display this will work OK, because y=1.5 and y=2.5 are pixel aligned:

However on a non-retina display the graphics system will be forced to fill 2 vertical pixels at half-intensity to get the closest match it can:

Note that this effect will still be seen on retina displays when you are dealing with smaller fractional point values. So this problems can still be seen on retina displays depending on your view's frame.

To fix this problem, any time you have an odd line width, you need to offset the point value where you draw it by 0.5. You need to offset it up/down/left/right depending on the situation.

I hope this helps. Without a screenshot it's hard to say what your problem is.