iOS绘制CALayers中的drawInContext在Retina屏幕上要么呈现为像素化,要么呈现为模糊不清。

22
我有一个自定义的UIView,它在覆盖后绘制一些东西。
- (void)drawRect:(CGRect)rect

这在视网膜屏幕上可以很好地工作并提供清晰的结果。

但是,现在我想使绘画基于的属性具有动画效果。创建可动画属性似乎只能在CALayer上实现,因此我不再使用UIView进行绘画,而是创建一个自定义的CALayer子类并在其中绘制。

- (void) drawInContext:(CGContextRef)ctx

在这个函数中,我使用的绘图代码与自定义 UIViewdrawRect 函数中使用的几乎完全相同。

结果看起来一样 - 但它不是retina分辨率,而是像素化的(大正方形像素)

如果我放置

self.contentsScale = [UIScreen mainScreen].scale;

在我的drawInContext实现的开头,如果出现了像素化的结果,那么我会得到一个模糊的结果(就好像渲染仍然是以非Retina分辨率进行的,然后被放大到Retina分辨率)。

CALayerdrawInContext中,如何正确地呈现清晰的Retina路径?

这里有一些屏幕截图(蓝线是所讨论的自定义绘制部分。黄色部分只是一张图片)

在自定义UIView的drawRect内绘制:

enter image description here

在自定义CALayer的drawInContext内绘制:

enter image description here

在自定义CALayer的drawInContext内绘制,并先设置self.contentScale

enter image description here

为了完整起见,这里是(剥离版本的)绘图代码:

//if drawing inside custom UIView sublcass:
- (void)drawRect:(CGRect)rect
{
    CGContextRef currenctContext = UIGraphicsGetCurrentContext();
    [[UIColor blackColor] set];

    CGContextSetLineWidth(currenctContext, _lineWidth);
    CGContextSetLineJoin(currenctContext,kCGLineJoinRound);

    CGContextMoveToPoint(currenctContext,x1, y1);
    CGContextAddLineToPoint(currenctContext,x2, y2);
    CGContextStrokePath(currenctContext);
}

//if drawing inside custom CALayer subclass:
- (void) drawInContext:(CGContextRef)ctx {
{
    //self.contentsScale = [UIScreen mainScreen].scale;

    CGContextRef currenctContext = ctx;
    CGContextSetStrokeColorWithColor(currenctContext, [UIColor blackColor].CGColor);

    CGContextSetLineWidth(currenctContext, _lineWidth);
    CGContextSetLineJoin(currenctContext,kCGLineJoinRound);

    CGContextMoveToPoint(currenctContext,x1, y1);
    CGContextAddLineToPoint(currenctContext,x2, y2);
    CGContextStrokePath(currenctContext);
}
为了重申我想要达到的目标:我希望在使用CALayer渲染时实现与UIView方法相同的清晰的视网膜渲染效果。

在视网膜显示屏上,你需要将高度和宽度乘以2。 - Frans Raharja Kurniawan
不,屏幕上的大小和位置是正确的。 - matthias_buehlmann
我是指您刚创建的屏幕应该乘以2,因为Retina显示屏需要双倍分辨率。 - Frans Raharja Kurniawan
1
谢谢你的帮助,但我认为你没有正确理解我的问题。 - matthias_buehlmann
2
如果将 self.contentsScale = [UIScreen mainScreen].scale 移动到图层初始化方法中会怎样? - orkenstein
3
你是否解决了这个问题?我也遇到完全相同的问题,使用 shouldRasterize 和设置 rasterizationScale 都没有效果。 - Rob Booth
3个回答

18

问题很可能是contentScale;请注意,如果您通过覆盖其layerClass函数将其分配给自定义视图,则图层的内容比例可能会重置。在其他情况下也可能发生这种情况。为了安全起见,请在将层添加到视图后再设置内容比例。

尝试在自定义视图的init方法中将主屏幕的比例分配给您的自定义图层。在Swift 3中,代码如下:

layer.contentsScale = UIScreen.mainScreen().scale

或者,在 Swift 4 中:

layer.contentsScale = UIScreen.main.scale

完美,这解决了像素化的问题。 - thecoolwinter
问题已解决,谢谢!Swift 5 2021 - StackGU

9

您是否在图层中使用shouldRasterize = YES?尝试在CALayer子类中绘制,但将rasterizationScale设置为屏幕比例。


@AndresCanella 你在哪个设备上使用了4.0的rasterizationScale? - Crashalot
@Crashalot 我最终在i6上使用了屏幕比例*2.0。在4.0上我失去了清晰度。 - Andres Canella

5

在将图层添加到其父图层后,将shouldRasterize属性设为YES,将contentsScale和rasterizationScale属性设置为屏幕比例:

[self.progressView.layer addSublayer:self.progressLayer];
self.progressLayer.shouldRasterize = YES;
self.progressLayer.contentsScale = kScreenScale;
self.progressLayer.rasterizationScale = kScreenScale;

CABasicAnimation *animate = [CABasicAnimation animationWithKeyPath:@"progress"];// progress is a customized property of progressLayer
animate.duration = 1.5;
animate.beginTime = 0;
animate.fromValue = @0;
animate.toValue = @1;
animate.fillMode = kCAFillModeForwards;
animate.removedOnCompletion = NO;
animate.repeatCount = HUGE_VALF;
[self.progressLayer addAnimation:animate forKey:@"progressAnimation"];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接