CALayer drawInContext无法在视网膜显示器上绘制1px线

时间:2013-05-02 06:09:30

标签: iphone ios objective-c calayer pixel

我想画两条不同颜色的1px线双色线。 我有很多代码,所以我在尝试解释之后就把它放了。

如果我使用uiview drawRect方法,当我关闭抗锯齿时它可以正常工作但是当我使用图层drawLayerInContext时,它会显示一条普通线,或者2条普通线条,2px,抗锯齿关闭或两条半透明的2px线,带有防眩光。

我通过使用自定义上下文创建一个可以指定比例的图像,设法获得了与UIView的drawRect方法类似的行为:

UIGraphicsBeginImageContextWithOptions([self bounds].size, NO, 0.f); // 0.f for scale means "scale for device's main screen".

我只想知道为什么我在drawInContext中没有得到相同的行为,并且是否有办法获得类似的行为。

以下是绘制双色线的代码:

void DRAW_DOUBLE_LINE(CGContextRef ctx, CGPoint startPoint, CGPoint endPoint, UIColor* topColor, UIColor* bottomColor)
{
    UIGraphicsPushContext(ctx);

    UIBezierPath *topLine = [[UIBezierPath alloc] init];
    CGPoint topLineStartPoint = startPoint;
    CGPoint topLineEndPoint = endPoint;
    [topLine moveToPoint:topLineStartPoint];
    [topLine addLineToPoint:topLineEndPoint];
    [topColor setStroke];
    topLine.lineWidth = 0.5;
    [topLine stroke];

    UIBezierPath *bottomLine = [[UIBezierPath alloc] init];
    CGPoint bottomLineStartPoint = topLineStartPoint;
    bottomLineStartPoint.y +=0.5;
    CGPoint bottomLineEndPoint = topLineEndPoint;
    bottomLineEndPoint.y +=0.5;
    [bottomLine moveToPoint:bottomLineStartPoint];
    [bottomLine addLineToPoint:bottomLineEndPoint];
    [bottomColor setStroke];
    bottomLine.lineWidth = 0.5;
    [bottomLine stroke];

    UIGraphicsPopContext();
}

使用drawRect UIView方法,我得到了这个:

| Points y coordinate | Antialiasing | Result                          |
| ------------------- | ------------ | ------------------------------- |
| 5                   | NO           | 2 lines of 1 px: Bingo!         |
| 5.25                | NO           | 2 lines of 1 px: Bingo!         |
| 5.5                 | NO           | 2 lines of 1 px: Bingo!         |
| 5                   | YES          | 3 half transparent lines of 1px |
| 5.25                | YES          | 2 lines of 1 px: Bingo!         |
| 5.5                 | YES          | 3 half transparent lines of 1px |

在带有drawInContext的CALayer中,我得到了这些结果

| Points y coordinate | Antialiasing | Result                          |
| ------------------- | ------------ | ------------------------------- |
| 5                   | NO           | 2 lines of 2 px                 |
| 5.25                | NO           | 1 line of 2 px                  |
| 5.5                 | NO           | 1 line of 2 px                  |
| 5                   | YES          | 2 half transparent lines of 2px |
| 5.25                | YES          | 1 half transparent line of 2px  |
| 5.5                 | YES          | 2 half transparent lines of 2px |

使用我的自定义上下文,我得到了这个:

| Points y coordinate | Antialiasing | Result                          |
| ------------------- | ------------ | ------------------------------- |
| 5                   | NO           | 2 lines of 1 px: Bingo!        |
| 5.25                | NO           | 2 lines of 1 px: Bingo!        |
| 5.5                 | NO           | 2 lines of 1 px: Bingo!        |
| 5                   | YES          | 3 half transparent lines of 1px |
| 5.25                | YES          | 2 lines of 1 px: Bingo!        |
| 5.5                 | YES          | 3 half transparent lines of 1px |

drawRect实施代码:

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGPoint startPoint = CGPointMake(0, 5);
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5);

    UIColor* topLineColor = [UIColor whiteColor];
    UIColor* bottomLineColor = [UIColor blackColor];

    DRAW_DOUBLE_LINE(context, startPoint, endPoint, topLineColor, bottomLineColor);
}

drawInContext实施代码:

-(void)drawInContext:(CGContextRef)ctx{
    CGPoint startPoint = CGPointMake(0, 5);
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5);

    UIColor* topLineColor = [UIColor whiteColor];
    UIColor* bottomLineColor = [UIColor blackColor];

    DRAW_DOUBLE_LINE(ctx, startPoint, endPoint, topLineColor, bottomLineColor);
}

CALayer display方法中自定义上下文实现的代码:

-(void)display{

    if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions([self bounds].size, NO, 0.f); // 0.f for scale means "scale for device's main screen".
    } else {
        UIGraphicsBeginImageContext([self bounds].size);
    }

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGPoint startPoint = CGPointMake(0, 5.25);
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5.25);
    UIColor* topLineColor = [UIColor whiteColor];
    UIColor* bottomLineColor = [UIColor blackColor];

    DRAW_DOUBLE_LINE(ctx, startPoint, endPoint, topLineColor, bottomLineColor);

    UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    self.contents = (id)coloredImg.CGImage;
}

4 个答案:

答案 0 :(得分:1)

不要使用0.5作为宽度。

0.5替换为1.0 / [UIScreen mainScreen].scale

在图层上绘制时,可以获得1像素宽的线。

答案 1 :(得分:0)

我建议不要绘制线条,只需绘制所需大小的填充矩形。具有不同分辨率的棘手部分之一是线的起点和终点的位置。但绘制矩形要容易得多。

矩形的坐标始终是偶数,因为您的目标是边缘,而不是像素的中心。

答案 2 :(得分:0)

绘制并填充矩形而不是线条。矩形的坐标应始终位于像素的边缘而不是中心。

答案 3 :(得分:0)

我发现最好的方法是在视网膜/非视网膜上获得精确/清晰的1px线,并将它们保持为1px,即使你放大也是为了写一个着色器。就我而言,我使用的是OpenGL,但我相信你可以用Metal for CALayers做到这一点。基本过程是绘制零高度的矩形(如果线是垂直的,则为零宽度),然后在着色器中将这些点向外推动所需的像素数。

我从这里得到了这个想法:https://www.mapbox.com/blog/drawing-antialiased-lines/