Рисунок iOS в CALayers drawInContext либо пикселирован, либо размыт на сетчатке

У меня есть пользовательский UIView который рисует что-то в перезаписанном

- (void)drawRect:(CGRect)rect

это прекрасно работает и дает четкие результаты на экранах сетчатки.

Однако теперь я хотел бы сделать свойства, на которых основан чертеж, анимируемыми. Создание анимируемых свойств представляется возможным только в CALayer, поэтому вместо рисования в UIView я создаю собственный подкласс CALayer и выполняю рисование внутри

- (void) drawInContext:(CGContextRef)ctx

В этой функции я использую практически тот же код рисования, что и в функции drawRect пользовательского UIView.

Результат выглядит одинаково - однако, это не разрешение сетчатки, а пиксельное (большие квадратные пиксели)

если я положу

self.contentsScale = [UIScreen mainScreen].scale;

В начале моей реализации drawInContext, затем вместо пиксельного результата я получаю размытый результат (как будто рендеринг по-прежнему выполняется в разрешении без сетчатки, а затем масштабируется до разрешения сетчатки).

Какой правильный способ визуализации острых путей сетчатки в CALayers drawInContext?

вот несколько скриншотов (синяя линия - часть рассматриваемого пользовательского рисунка. желтая часть - просто изображение)

Нарисовано внутри пользовательского 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);
}

Чтобы переформулировать то, чего я хочу достичь: я хочу добиться того же четкого рендеринга сетчатки, что и в подходе UIView, но при рендеринге в CALayer

Ответы

Ответ 1

Используете ли вы shouldRasterize = YES в слое? Попробуйте выполнить рисование в подклассе CALayer, но установите rasterizationScale на экранную шкалу.

Ответ 2

Проблема, скорее всего, будет contentScale здесь; имейте в виду, что если вы назначаете это настраиваемому представлению, переопределяя его функцию layerClass, масштаб содержимого слоя может быть reset. Могут быть и другие случаи, в которых это также происходит. Чтобы быть в безопасности, установите масштаб содержимого только после добавления слоя в представление.

Попробуйте назначить основной экран на свой пользовательский уровень во время вашего настраиваемого метода init. В Swift 3 выглядит так:

layer.contentsScale = UIScreen.mainScreen().scale

Или, в Swift 4:

layer.contentsScale = UIScreen.main.scale

Ответ 3

После добавления слоя к его суперслою. set shouldRasterize to YES, установить содержимоеScale и resterizatioinScale для масштабирования экрана:

[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"];