Come ottenere il testo in un CATextLayer per essere chiari


92

Ne ho fatta una CALayercon aggiunta CATextLayere il testo risulta sfocato. Nei documenti si parla di "antialiasing sub-pixel", ma per me non significa molto. Qualcuno ha uno snippet di codice che fa un CATextLayercon un po 'di testo che è chiaro?

Ecco il testo dalla documentazione di Apple:

Nota: CATextLayer disabilita l'antialias dei sub-pixel durante il rendering del testo. Il testo può essere disegnato utilizzando l'antialias sub-pixel solo quando viene composto in uno sfondo opaco esistente nello stesso momento in cui viene rasterizzato. Non c'è modo di disegnare da solo il testo con antialias dei subpixel, sia in un'immagine che in un livello, separatamente prima di avere i pixel di sfondo in cui intrecciare i pixel di testo. L'impostazione della proprietà di opacità del livello su SÌ non modifica la modalità di rendering.

La seconda frase implica che si può ottenere un bel testo se lo si compositesinserisce in existing opaque background at the same time that it's rasterized. È fantastico, ma come lo compongo e come gli dai uno sfondo opaco e come lo rasterizzi?

Il codice che usano nel loro esempio di menu Kiosk è come tale: (È OS X, non iOS, ma presumo che funzioni!)

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 

Grazie!


EDIT: l'aggiunta del codice che ho effettivamente usato ha prodotto un testo sfocato. È da una domanda correlata che ho postato sull'aggiunta UILabeldi una CATextLayerscatola nera anziché su una scatola nera. 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: vedi la mia risposta qui sotto per come è stato risolto. sbg.


Questo non rispondere alla tua domanda, ma può essere rilevante per uno come me che sta solo cercando di disegnare il testo attribuito, nel modo più simile ad usare UILabel: stackoverflow.com/questions/3786528/...
DenNukem

Risposte:


228

Risposta breve: è necessario impostare il ridimensionamento dei contenuti:

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

Tempo fa ho appreso che quando si dispone di un codice di disegno personalizzato, è necessario verificare la visualizzazione della retina e ridimensionare la grafica di conseguenza. UIKitsi occupa della maggior parte di questo, incluso il ridimensionamento dei caratteri.

Non così con CATextLayer.

La mia sfocatura derivava dall'avere un .zPositionche non era zero, cioè avevo una trasformazione applicata al mio livello genitore. Impostando questo valore su zero, la sfocatura è scomparsa ed è stata sostituita da una grave pixelizzazione.

Dopo aver cercato in alto e in basso, ho scoperto che puoi impostare .contentsScalea CATextLayere puoi impostarlo in modo che [[UIScreen mainScreen] scale]corrisponda alla risoluzione dello schermo. (Presumo che funzioni per la non retina, ma non ho controllato - troppo stanco)

Dopo aver incluso questo per me, CATextLayeril testo è diventato nitido. Nota: non è necessario per il livello principale.

E la sfocatura? Ritorna quando stai ruotando in 3D, ma non te ne accorgi perché il testo inizia in modo chiaro e mentre è in movimento, non puoi dirlo.

Problema risolto!


37
Questa è la risposta breve: A textLayer.contentScale = [[UIScreen mainScreen] scale]; proposito, mi piacciono anche le risposte lunghe :)
nacho4d

Ciò si applica a qualsiasi CALayer creato a livello di programmazione. Ad esempio, se noti che le immagini disegnate nel tuo CALayer non sono nitide sul display retina, è probabile che questo sia il problema e la soluzione.
Jeff Argast

17
Correzione _textLayer.contentsScale = [[UIScreen mainScreen] scale]; Ti manca una "s";)
Ralphleon

1
Questo non funziona per me. Aggiungo un CATextLayer a un video (AVFoundation / AVMutableComposition) di AVVideoCompositionCoreAnimationTool. Il testo ha ancora l'alias.
Vietstone

non esiste un'opzione di scala in mac osx
Sangram Shivankar

35

Swift

Imposta il livello del testo in modo che utilizzi la stessa scala dello schermo.

textLayer.contentsScale = UIScreen.main.scale

Prima:

inserisci qui la descrizione dell'immagine

Dopo:

inserisci qui la descrizione dell'immagine


Versione macOS swift 4.2: (NSScreen.main? .backingScaleFactor)!
roberto

17

Prima di tutto volevo sottolineare che hai taggato la tua domanda con iOS, ma i gestori dei vincoli sono disponibili solo su OSX, quindi non sono sicuro di come far funzionare questo a meno che tu non sia stato in grado di collegarti ad esso in qualche modo nel simulatore. Sul dispositivo, questa funzionalità non è disponibile.

Successivamente, mi limiterò a sottolineare che creo spesso CATextLayer e non ho mai il problema di sfocatura a cui ti riferisci, quindi so che può essere fatto. In poche parole, questa sfocatura si verifica perché non stai posizionando il tuo livello sull'intero pixel. Ricorda che quando imposti la posizione di un livello, usi valori float per x e y. Se questi valori hanno numeri dopo il decimale, il livello non sarà posizionato sull'intero pixel e darà quindi questo effetto di sfocatura, il cui grado dipende dai valori effettivi. Per verificarlo, crea semplicemente un CATextLayer e aggiungilo esplicitamente all'albero dei livelli assicurandoti che il tuo parametro di posizione sia su un intero pixel. Per esempio:

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 il tuo testo è ancora sfocato, c'è qualcos'altro che non va. Il testo sfocato sul livello di testo è un artefatto di codice scritto in modo errato e non una capacità prevista del livello. Quando aggiungi i tuoi livelli a un livello principale, puoi semplicemente forzare i valori xey al pixel intero più vicino e dovrebbe risolvere il tuo problema di sfocatura.


15

Prima di impostare shouldRasterize, dovresti:

  1. imposta la scala di rasterizzazione del livello di base che intendi rasterizzare
  2. impostare la proprietà contentsScale di qualsiasi CATextLayer e possibilmente altri tipi di livelli (non fa mai male farlo)

Se non esegui il n. 1, la versione retina dei sottostrati apparirà sfocata, anche per i normali CALayer.

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

Dovresti fare 2 cose, la prima è stata menzionata sopra:

Estendi CATextLayer e imposta le proprietà opaque e contentsScale per supportare correttamente la visualizzazione retina, quindi renderizza con anti aliasing abilitato per il testo.

+ (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];
}

3

Se sei venuto a cercare qui un problema simile per un CATextLayer in OSX, dopo molti colpi di testa sul muro, ho ottenuto il testo nitido e chiaro facendo:

text_layer.contentsScale = self.window!.backingScaleFactor

(Ho anche impostato la scala del contenuto del livello di sfondo delle viste in modo che sia la stessa).


0

Questo è più veloce e più facile: devi solo impostare contentScale

    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];
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.