Como fazer com que o texto em uma CATextLayer seja claro

92

Fiz um CALayercom um adicionado CATextLayere o texto sai borrado. Nos documentos, eles falam sobre "suavização de subpixel", mas isso não significa muito para mim. Alguém tem um trecho de código que faz um CATextLayercom um trecho de texto claro?

Aqui está o texto da documentação da Apple:

Nota: CATextLayer desativa suavização de subpixel ao renderizar texto. O texto só pode ser desenhado usando suavização de subpixel quando é composto em um fundo opaco existente ao mesmo tempo em que é rasterizado. Não há como desenhar o texto suavizado de subpixel por si só, seja em uma imagem ou uma camada, separadamente antes de ter os pixels de fundo para tecer os pixels de texto. Definir a propriedade opacidade da camada como SIM não altera o modo de renderização.

A segunda frase implica que é possível obter um texto com boa aparência se convertê- compositeslo em um existing opaque background at the same time that it's rasterized. Isso é ótimo, mas como faço para compô-lo e como dar a ele um fundo opaco e como rasterizá-lo?

O código que eles usam em seu exemplo de menu de quiosque é o seguinte: (É OS X, não iOS, mas presumo que funcione!)

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 

Obrigado!


EDIT: Adicionando o código que eu realmente usei que resultou em texto borrado. É de uma questão relacionada que postei sobre adicionar um em UILabelvez de um, CATextLayermas obter uma caixa preta. 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);

EDIT 2: Veja minha resposta abaixo para saber como isso foi resolvido. sbg.

Steve
fonte
Isso não vai responder à sua pergunta, mas pode ser relevante para alguém como eu, que está apenas procurando desenhar um texto atribuído, de uma maneira mais semelhante ao uso de UILabel: stackoverflow.com/questions/3786528/…
DenNukem

Respostas:

228

Resposta curta - você precisa definir a escala de conteúdo:

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

Há algum tempo, aprendi que, quando você tem um código de desenho personalizado, precisa verificar a tela retina e dimensionar seus gráficos de acordo. UIKitcuida da maior parte disso, incluindo o dimensionamento da fonte.

Não é assim com CATextLayer.

Meu borrão veio de ter um .zPositionque não era zero, ou seja, eu tinha uma transformação aplicada à minha camada pai. Ao definir isso para zero, o borrão foi embora e foi substituído por pixelização grave.

Depois de pesquisar alto e baixo, descobri que você pode definir .contentsScaleum CATextLayere para [[UIScreen mainScreen] scale]que corresponda à resolução da tela. (Presumo que isso funcione para não retina, mas não verifiquei - muito cansado)

Depois de incluir isso no meu, CATextLayero texto ficou nítido. Observação - não é necessário para a camada pai.

E o borrão? Ele volta quando você está girando em 3D, mas você não percebe porque o texto começa claro e, enquanto está em movimento, não dá para perceber.

Problema resolvido!

Steve
fonte
37
Esta é a resposta curta: textLayer.contentScale = [[UIScreen mainScreen] scale]; BTW, eu também gosto de respostas longas :)
nacho4d
Isso se aplica a qualquer CALayer criada programaticamente. Por exemplo, se você notar que as imagens desenhadas em sua CALayer não são nítidas na tela retina, é provável que este seja o problema e a solução.
Jeff Argast
17
Correção _textLayer.contentsScale = [[UIScreen mainScreen] scale]; Está faltando um "s";)
Ralphleon
1
Isso não funciona para mim. Eu adiciono um CATextLayer a um vídeo (AVFoundation / AVMutableComposition) por AVVideoCompositionCoreAnimationTool. O texto ainda tem aliasing.
Vietnam
não há opção de escala no mac osx
Sangram Shivankar
35

Rápido

Defina a camada de texto para usar a mesma escala da tela.

textLayer.contentsScale = UIScreen.main.scale

Antes:

insira a descrição da imagem aqui

Depois de:

insira a descrição da imagem aqui

Suragch
fonte
versão 4.2 do macOS swift: (NSScreen.main? .backingScaleFactor)!
roberto
17

Em primeiro lugar, gostaria de salientar que você marcou sua pergunta com iOS, mas os gerenciadores de restrições estão disponíveis apenas no OSX, então não tenho certeza de como você está fazendo isso funcionar, a menos que tenha conseguido um link para ele no simulador de alguma forma. No dispositivo, essa funcionalidade não está disponível.

A seguir, vou apenas apontar que eu crio CATextLayers com frequência e nunca tenho o problema de desfoque a que você está se referindo, então sei que isso pode ser feito. Resumindo, esse desfoque ocorre porque você não está posicionando sua camada no pixel inteiro. Lembre-se de que ao definir a posição de uma camada, você usa valores flutuantes para x e y. Se esses valores tiverem números após o decimal, a camada não será posicionada em todo o pixel e, portanto, dará esse efeito de desfoque - cujo grau depende dos valores reais. Para testar isso, basta criar um CATextLayer e adicioná-lo explicitamente à árvore de camadas garantindo que seu parâmetro de posição esteja em um pixel inteiro. Por exemplo:

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

Se o seu texto ainda estiver borrado, há algo errado. O texto desfocado em sua camada de texto é um artefato de código escrito incorretamente e não um recurso pretendido da camada. Ao adicionar suas camadas a uma camada pai, você pode apenas forçar os valores xey para o pixel inteiro mais próximo e isso deve resolver seu problema de desfoque.

Matt Long
fonte
15

Antes de definir o shouldRasterize, você deve:

  1. defina a escala de rasterização da camada de base que você vai rasterizar
  2. defina a propriedade contentsScale de quaisquer CATextLayers e possivelmente outros tipos de camadas (nunca é demais fazer isso)

Se você não fizer o nº 1, a versão retina das subcamadas parecerá borrada, mesmo para CALayers normais.

- (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
fonte
4

Você deve fazer 2 coisas, a primeira foi mencionada acima:

Estenda CATextLayer e defina as propriedades opaque e contentsScale para suportar adequadamente a exibição de retina e, em seguida, renderize com suavização de serrilhado ativada para 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
fonte
3

Se você veio procurando aqui por um problema semelhante para um CATextLayer no OSX, depois de muito bater cabeça na parede, obtive o texto claro nítido fazendo:

text_layer.contentsScale = self.window!.backingScaleFactor

(Eu também defino o conteúdo da camada de plano de fundo das visualizações como o mesmo)

james_alvarez
fonte
0

Isso é mais rápido e fácil: você só precisa definir a escala de conteúdo

    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
fonte