Как сделать текст в CATextLayer понятным

92

Я сделал CALayerс добавлением, CATextLayerи текст получается размытым. В документации говорится о «субпиксельном сглаживании», но для меня это мало что значит. У кого-нибудь есть фрагмент кода, который делает CATextLayerнебольшой текст понятным?

Вот текст из документации Apple:

Примечание. CATextLayer отключает субпиксельное сглаживание при визуализации текста. Текст может быть нарисован с использованием субпиксельного сглаживания только тогда, когда он объединяется в существующий непрозрачный фон одновременно с растеризацией. Невозможно нарисовать текст с субпиксельным сглаживанием отдельно, будь то изображение или слой, отдельно до наличия пикселей фона, в которые будут вплетаться пиксели текста. Установка свойства непрозрачности слоя на ДА не изменяет режим рендеринга.

Второе предложение подразумевает, что можно получить красивый текст, если compositesпревратить его в. existing opaque background at the same time that it's rasterized. Отлично, но как мне составить его, как сделать его непрозрачным фоном и как растеризовать его?

Код, который они используют в своем примере меню киоска, выглядит следующим образом: (Это OS X, а не iOS, но я предполагаю, что он работает!)

NSInteger i;
for (i=0;i<[names count];i++) {
    CATextLayer *menuItemLayer=[CATextLayer layer];
    menuItemLayer.string=[self.names objectAtIndex:i];
    menuItemLayer.font=@"Lucida-Grande";
    menuItemLayer.fontSize=fontSize;
    menuItemLayer.foregroundColor=whiteColor;
    [menuItemLayer addConstraint:[CAConstraint
         constraintWithAttribute:kCAConstraintMaxY
                      relativeTo:@"superlayer"
                       attribute:kCAConstraintMaxY
                          offset:-(i*height+spacing+initialOffset)]];
    [menuItemLayer addConstraint:[CAConstraint
         constraintWithAttribute:kCAConstraintMidX
                      relativeTo:@"superlayer"
                       attribute:kCAConstraintMidX]];
    [self.menuLayer addSublayer:menuItemLayer];
} // end of for loop 

Благодарность!


РЕДАКТИРОВАТЬ: Добавление кода, который я действительно использовал, привело к размытому тексту. Это от родственного вопроса , который я разместил о добавлении , UILabelа не CATextLayerтолько получить черный ящик вместо этого. http://stackoverflow.com/questions/3818676/adding-a-uilabels-layer-to-a-calayer-and-it-just-shows-black-box

CATextLayer* upperOperator = [[CATextLayer alloc] init];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGFloat components1[4] = {1.0, 1.0, 1.0, 1.0};
CGColorRef almostWhite = CGColorCreate(space,components1);
CGFloat components2[4] = {0.0, 0.0, 0.0, 1.0};
CGColorRef almostBlack = CGColorCreate(space,components2);
CGColorSpaceRelease(space);
upperOperator.string = [NSString stringWithFormat:@"13"];
upperOperator.bounds = CGRectMake(0, 0, 100, 50);
upperOperator.foregroundColor = almostBlack;
upperOperator.backgroundColor = almostWhite;
upperOperator.position = CGPointMake(50.0, 25.0);
upperOperator.font = @"Helvetica-Bold";
upperOperator.fontSize = 48.0f;
upperOperator.borderColor = [UIColor redColor].CGColor;
upperOperator.borderWidth = 1;
upperOperator.alignmentMode = kCAAlignmentCenter;
[card addSublayer:upperOperator];
[upperOperator release];
CGColorRelease(almostWhite);
CGColorRelease(almostBlack);

РЕДАКТИРОВАТЬ 2: См. Мой ответ ниже, чтобы узнать, как это было решено. сбг.

Стив
источник
Это не ответит на ваш вопрос, но может быть актуальным для кого-то вроде меня, кто хочет рисовать только атрибутивный текст способом, наиболее похожим на использование UILabel: stackoverflow.com/questions/3786528/…
DenNukem

Ответы:

229

Краткий ответ - Вам необходимо настроить масштабирование содержимого:

textLayer.contentsScale = [[UIScreen mainScreen] scale];

Некоторое время назад я узнал, что когда у вас есть собственный код рисования, вы должны проверять отображение сетчатки и соответствующим образом масштабировать графику. UIKitберет на себя большую часть этого, включая масштабирование шрифта.

Не так с CATextLayer.

Моя размытость возникла из-за того, .zPositionчто не было нуля, то есть я применил преобразование к моему родительскому слою. Установив это значение на ноль, размытость исчезла и была заменена серьезной пикселизацией.

После поиска высокого и низкого я обнаружил, что вы можете установить .contentsScaleдля, CATextLayerа вы можете установить его [[UIScreen mainScreen] scale]в соответствии с разрешением экрана. (Я предполагаю, что это работает для не сетчатки глаза, но я не проверял - слишком устал)

После включения этого для меня CATextLayerтекст стал четким. Обратите внимание - для родительского слоя это не обязательно.

А размытость? Он возвращается, когда вы вращаетесь в 3D, но вы этого не замечаете, потому что текст сначала четкий, а пока он движется, вы не можете сказать.

Задача решена!

Стив
источник
37
Это короткий ответ: textLayer.contentScale = [[UIScreen mainScreen] scale]; Кстати, мне тоже нравятся длинные ответы :)
nacho4d
Это применимо к любому CALayer, созданному программно. Например, если вы замечаете, что изображения, нарисованные в вашем CALayer, не четкие на дисплее сетчатки, вероятно, это проблема и решение.
Джефф Аргаст
17
Исправление _textLayer.contentsScale = [[UIScreen mainScreen] scale]; У вас отсутствует
буква
1
У меня это не работает. Я добавляю CATextLayer к видео (AVFoundation / AVMutableComposition) с помощью AVVideoCompositionCoreAnimationTool. Текст по-прежнему имеет алиасинг.
vietstone
в mac osx нет опции масштабирования
Санграм Шиванкар
35

Swift

Установите для текстового слоя такой же масштаб, что и на экране.

textLayer.contentsScale = UIScreen.main.scale

Перед:

введите описание изображения здесь

После:

введите описание изображения здесь

Suragch
источник
Версия macOS Swift 4.2: (NSScreen.main? .backingScaleFactor)!
Роберто
17

Прежде всего, я хотел указать, что вы пометили свой вопрос с помощью iOS, но менеджеры ограничений доступны только в OSX, поэтому я не уверен, как вы заставляете это работать, если вы не смогли связать с ним в симуляторе как-то. На устройстве эта функция недоступна.

Далее я просто отмечу, что я часто создаю CATextLayers и никогда не сталкивался с проблемой размытия, о которой вы говорите, поэтому я знаю, что это можно сделать. Вкратце, это размытие происходит из-за того, что вы не размещаете свой слой на всем пикселе. Помните, что когда вы устанавливаете положение слоя, вы используете значения с плавающей запятой для x и y. Если эти значения имеют числа после десятичной дроби, слой не будет располагаться на всем пикселе и, следовательно, даст эффект размытия, степень которого зависит от фактических значений. Чтобы проверить это, просто создайте CATextLayer и явно добавьте его в дерево слоев, убедившись, что ваш параметр позиции находится на всем пикселе. Например:

CATextLayer *textLayer = [CATextLayer layer];
[textLayer setBounds:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)];
[textLayer setPosition:CGPointMake(200.0f, 100.0f)];
[textLayer setString:@"Hello World!"];

[[self menuLayer] addSublayer:textLayer];

Если ваш текст по-прежнему размыт, значит, что-то не так. Размытый текст на текстовом слое - это артефакт неправильно написанного кода, а не предполагаемая возможность слоя. При добавлении слоев к родительскому слою вы можете просто привести значения x и y к ближайшему целому пикселю, и это должно решить вашу проблему размытия.

Мэтт Лонг
источник
15

Перед настройкой shouldRasterize необходимо:

  1. установите rasterizationScale базового слоя, который вы собираетесь растрировать
  2. установить свойство contentsScale любого CATextLayers и, возможно, других типов слоев (это никогда не повредит)

Если вы не сделаете №1, то версия подслоев сетчатки будет выглядеть размытой даже для обычных CALayers.

- (void)viewDidLoad {
  [super viewDidLoad];

  CALayer *mainLayer = [[self view] layer];
  [mainLayer setRasterizationScale:[[UIScreen mainScreen] scale]];

  CATextLayer *messageLayer = [CATextLayer layer];
  [messageLayer setForegroundColor:[[UIColor blackColor] CGColor]];
  [messageLayer setContentsScale:[[UIScreen mainScreen] scale]];
  [messageLayer setFrame:CGRectMake(50, 170, 250, 50)];
  [messageLayer setString:(id)@"asdfasd"];

  [mainLayer addSublayer:messageLayer];

  [mainLayer setShouldRasterize:YES];
}
Гейб
источник
4

Вы должны сделать 2 вещи, первая из которых была упомянута выше:

Расширьте CATextLayer и установите свойства opaque и contentsScale для правильной поддержки отображения сетчатки, а затем выполните рендеринг с включенным сглаживанием для текста.

+ (TextActionLayer*) layer
{
  TextActionLayer *layer = [[TextActionLayer alloc] init];
  layer.opaque = TRUE;
  CGFloat scale = [[UIScreen mainScreen] scale];
  layer.contentsScale = scale;
  return [layer autorelease];
}

// Render after enabling with anti aliasing for text

- (void)drawInContext:(CGContextRef)ctx
{
    CGRect bounds = self.bounds;
    CGContextSetFillColorWithColor(ctx, self.backgroundColor);
    CGContextFillRect(ctx, bounds);
    CGContextSetShouldSmoothFonts(ctx, TRUE);
    [super drawInContext:ctx];
}
MoDJ
источник
3

Если вы искали здесь аналогичную проблему для CATextLayer в OSX, после долгих ударов головой о стену я получил четкий четкий текст, выполнив:

text_layer.contentsScale = self.window!.backingScaleFactor

(Я также установил такое же значение ContentScale для фонового слоя представлений).

james_alvarez
источник
0

Это быстрее и проще: вам просто нужно установить contentsScale

    CATextLayer *label = [[CATextLayer alloc] init];
    [label setFontSize:15];
    [label setContentsScale:[[UIScreen mainScreen] scale]];
    [label setFrame:CGRectMake(0, 0, 50, 50)];
    [label setString:@"test"];
    [label setAlignmentMode:kCAAlignmentCenter];
    [label setBackgroundColor:[[UIColor clearColor] CGColor]];
    [label setForegroundColor:[[UIColor blackColor] CGColor]];
    [self addSublayer:label];
Алехандро Луенго
источник