设置 CAShapeLayer lineWidth 小于 1

Setting CAShapeLayer lineWidth Less Than 1

在上面的截图中,有两行:

  1. 实线只是一个UIView,高度为 1px
  2. 虚线是使用此代码创建的

- (void)viewDidAppear:(BOOL)animated {
    CAShapeLayer *line = [CAShapeLayer layer];
    UIBezierPath *linePath=[UIBezierPath bezierPath];

    [linePath moveToPoint:CGPointMake(0, 107)];
    [linePath addLineToPoint:CGPointMake(self.view.frame.size.width, 107)];

    line.lineWidth = 0.5;
    line.path=linePath.CGPath;
    line.fillColor = [[UIColor blackColor] CGColor];
    line.strokeColor = [[UIColor blackColor] CGColor];

    [line setLineJoin:kCALineJoinRound];
    [line setLineDashPattern: [NSArray arrayWithObjects:[NSNumber numberWithInt:10], [NSNumber numberWithInt:5],nil]];

    [[self.view layer] addSublayer:line];
}

为什么 1 像素 (1.0) 高度的 UIView 小于 0.5 高度的虚线?

我希望虚线和实线一样细。

0.5 分将转化为:

  • @3x 视网膜显示器上为 1.5px
  • 1px @2x 视网膜显示器
  • 0.5 正常显示

0.5 不是像素坐标系中的物理量度(半像素不存在),因此渲染系统通常会在所有呈现小数的地方创建抗锯齿。

放置 UIView 时也会发生这种情况,可能您已经在它们周围看到了不需要的模糊,当框架有小数点时会发生这种情况,通常可以通过在 [=11] 中传递框架来修复=]函数。
要绘制一条像素线,我引用 Apple indications about it:

On a high-resolution display (with a scale factor of 2.0), a line that is one point wide is not antialiased at all because it occupies two full pixels (from -0.5 to +0.5). To draw a line that covers only a single physical pixel, you would need to make it 0.5 points in thickness and offset its position by 0.25 points

当您说 UIView 的高度为 1px 时,实际上 是指 1px 吗?

UIKit 中的大小(CAShapeLayerUIViewlineWidthframe 分别在 points, not pixels 中测量。一个点在 1x 显示器上相当于 1 个像素,在 2x 显示器上相当于 2 个像素,在 3x 显示器上相当于 3 个像素。*

因此,如果您想要以磅为单位的单个像素的大小 - 您需要

1.0/[UIScreen mainScreen].scale

您的图像中似乎存在的问题是您将 UIView 的框架原点与 UIBezierPath 的线混淆了。这些不一样。框架原点代表您 UIViewtop — 而直线代表您路径的 centre

因此,您需要将线条位置向下偏移其宽度的一半 - 这会将其与像素的 中心 对齐,从而使笔划呈现在单个像素。**

像这样应该可以达到您想要的结果:

CGFloat pixelWidth = 1.0/[UIScreen mainScreen].scale;

UIView* v = [[UIView alloc] initWithFrame:(CGRect){0, 50, self.view.frame.size.width, pixelWidth}];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];

UIBezierPath* p = [UIBezierPath bezierPath];
[p moveToPoint:(CGPoint){0, 50+(pixelWidth*0.5)}];
[p addLineToPoint:(CGPoint){self.view.frame.size.width, 50+(pixelWidth*0.5)}];

CAShapeLayer* s = [CAShapeLayer layer];
s.contentsScale = [UIScreen mainScreen].scale; // ensures the CAShapeLayer renders its contents at the logical scale of the screen
s.frame = self.view.bounds;
s.path = p.CGPath;
s.fillColor = [UIColor clearColor].CGColor;
s.strokeColor = [UIColor greenColor].CGColor;
s.lineWidth = pixelWidth;
s.lineDashPattern = @[@10, @10];
[self.view.layer addSublayer:s];

生成以下内容(在 iPhone 6 上):


*iPhone 6 Plus 的行为略有不同 - 它的物理显示比例 (~2.6x) 与逻辑比例 (3x) 不匹配。

因此,您在其中进行的任何绘图 can result in pixel bleeding, as it gets scaled down to be displayed. You can get around this, but it involves delving into Open GL or Metal 都可以进行绘图。

See also here 可以很好地了解每个 iPhone 如何呈现其内容。


**在 2x 显示器上,您 可能 还需要将线条的位置额外偏移 0.25 点,以防止像素溢出(因为您的线条将位于像素边界上), .