Cómo hacer que el texto en un CATextLayer sea claro

92

Hice un CALayercon un agregado CATextLayery el texto sale borroso. En los documentos, se habla de "suavizado de subpíxeles", pero eso no significa mucho para mí. ¿Alguien tiene un fragmento de código que haga una CATextLayercon un poco de texto que sea claro?

Aquí está el texto de la documentación de Apple:

Nota: CATextLayer deshabilita el suavizado de subpíxeles al representar texto. El texto solo se puede dibujar usando suavizado de subpíxeles cuando se compone en un fondo opaco existente al mismo tiempo que se rasteriza. No hay forma de dibujar texto con suavizado de subpíxeles por sí mismo, ya sea en una imagen o una capa, por separado antes de tener los píxeles de fondo para entretejer los píxeles de texto. Establecer la propiedad de opacidad de la capa en SÍ no cambia el modo de renderizado.

La segunda oración implica que uno puede obtener un texto atractivo si uno composites lo convierte en un existing opaque background at the same time that it's rasterized. Eso es genial, pero ¿cómo lo compongo y cómo le da un fondo opaco y cómo lo rasteriza?

El código que usan en su ejemplo de un menú de quiosco es como tal: (es OS X, no iOS, ¡pero supongo que funciona!)

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 

¡Gracias!


EDITAR: Agregar el código que realmente usé que resultó en texto borroso. Es de una pregunta relacionada que publiqué sobre agregar un en UILabellugar de un, CATextLayerpero obtener una caja negra en su lugar. 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);

EDITAR 2: Vea mi respuesta a continuación para saber cómo se resolvió esto. sbg.

Steve
fuente
Esto no responderá a su pregunta, pero puede ser relevante para alguien como yo que solo busca dibujar texto atribuido, de la manera más similar a usar UILabel: stackoverflow.com/questions/3786528/…
DenNukem

Respuestas:

229

Respuesta corta: debe configurar la escala de contenido:

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

Hace un tiempo aprendí que cuando tienes un código de dibujo personalizado, debes verificar la pantalla de retina y escalar tus gráficos en consecuencia. UIKitse encarga de la mayor parte de esto, incluida la escala de fuentes.

No es así con CATextLayer.

Mi borrosidad vino de tener un .zPosition que no era cero, es decir, tenía una transformación aplicada a mi capa principal. Al establecer esto en cero, la borrosidad desapareció y fue reemplazada por una pixelación seria.

Después de buscar alto y bajo, descubrí que puede configurar .contentsScalea CATextLayery puede configurarlo[[UIScreen mainScreen] scale] que coincida con la resolución de la pantalla. (Supongo que esto funciona para no retina, pero no lo he comprobado, demasiado cansado)

Después de incluir esto para mi CATextLayer el texto se volvió nítido. Nota: no es necesario para la capa principal.

¿Y la borrosidad? Vuelve cuando estás rotando en 3D, pero no lo notas porque el texto comienza claro y mientras está en movimiento, no puedes saberlo.

¡Problema resuelto!

Steve
fuente
37
Esta es la respuesta corta: textLayer.contentScale = [[UIScreen mainScreen] scale]; Por cierto, a mí también me gustan las respuestas largas :)
nacho4d
Esto se aplica a cualquier CALayer que se crea mediante programación. Por ejemplo, si nota que las imágenes dibujadas en su CALayer no son nítidas en la pantalla de retina, es probable que este sea el problema y la solución.
Jeff Argast
17
Corrección _textLayer.contentsScale = [[UIScreen mainScreen] scale]; Falta una "s";)
Ralphleon
1
Esto no me funciona. Agrego un CATextLayer a un video (AVFoundation / AVMutableComposition) de AVVideoCompositionCoreAnimationTool. El texto todavía tiene alias.
vietstone
no hay opción de escala en mac osx
Sangram Shivankar
35

Rápido

Configure la capa de texto para usar la misma escala que la pantalla.

textLayer.contentsScale = UIScreen.main.scale

Antes de:

ingrese la descripción de la imagen aquí

Después:

ingrese la descripción de la imagen aquí

Suragch
fuente
macOS swift 4.2 versión: (NSScreen.main? .backingScaleFactor)!
roberto
17

En primer lugar, quería señalar que ha etiquetado su pregunta con iOS, pero los administradores de restricciones solo están disponibles en OSX, por lo que no estoy seguro de cómo está logrando que esto funcione a menos que haya podido vincularlo. en el simulador de alguna manera. En el dispositivo, esta funcionalidad no está disponible.

A continuación, solo señalaré que creo CATextLayers con frecuencia y nunca tengo el problema de desenfoque al que se refiere, así que sé que se puede hacer. En pocas palabras, este desenfoque se produce porque no está colocando su capa en todo el píxel. Recuerde que cuando establece la posición de una capa, usa valores flotantes para x e y. Si esos valores tienen números después del decimal, la capa no se colocará en todo el píxel y, por lo tanto, dará este efecto de desenfoque, cuyo grado dependerá de los valores reales. Para probar esto, simplemente cree un CATextLayer y agréguelo explícitamente al árbol de capas, asegurándose de que su parámetro de posición esté en un píxel completo. Por ejemplo:

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

Si su texto aún está borroso, entonces hay algo más mal. El texto borroso en su capa de texto es un artefacto de código escrito incorrectamente y no una capacidad prevista de la capa. Al agregar sus capas a una capa principal, puede simplemente forzar los valores xey al píxel completo más cercano y debería resolver su problema de desenfoque.

Matt Long
fuente
15

Antes de configurar shouldRasterize, debe:

  1. establece la escala de rasterización de la capa base que vas a rasterizar
  2. establecer la propiedad contentsScale de cualquier CATextLayers y posiblemente otros tipos de capas (nunca está de más hacerlo)

Si no hace el n. ° 1, la versión retina de las subcapas se verá borrosa, incluso para los CALayers normales.

- (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];
}
Gabe
fuente
4

Debes hacer 2 cosas, la primera se mencionó anteriormente:

Extienda CATextLayer y establezca las propiedades opaco y contentsScale para admitir correctamente la visualización de retina, luego renderice con el suavizado habilitado para el texto.

+ (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
fuente
3

Si vino a buscar aquí un problema similar para un CATextLayer en OSX, después de muchos golpes en la pared, obtuve el texto claro y nítido haciendo:

text_layer.contentsScale = self.window!.backingScaleFactor

(También configuré el contenido de la capa de fondo de las vistas Escala para que sea el mismo).

james_alvarez
fuente
0

Esto es más rápido y más fácil: solo necesita configurar el contenido

    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];
Alejandro Luengo
fuente