Comment obtenir du texte dans un CATextLayer pour être clair

92

J'ai fait un CALayeravec un ajouté CATextLayeret le texte est flou. Dans la documentation, ils parlent d '"anticrénelage sous-pixel", mais cela ne signifie pas grand-chose pour moi. Quelqu'un a un extrait de code qui fait un CATextLayeravec un peu de texte qui est clair?

Voici le texte de la documentation d'Apple:

Remarque: CATextLayer désactive l'anticrénelage des sous-pixels lors du rendu du texte. Le texte ne peut être dessiné à l'aide de l'anticrénelage sous-pixel que lorsqu'il est composé dans un arrière-plan opaque existant en même temps qu'il est pixellisé. Il n'y a aucun moyen de dessiner un texte antialiasé de sous-pixels par lui-même, que ce soit dans une image ou un calque, séparément avant d'avoir les pixels d'arrière-plan dans lesquels tisser les pixels du texte. La définition de la propriété d'opacité du calque sur OUI ne modifie pas le mode de rendu.

La deuxième phrase implique que l'on peut obtenir un beau texte si l' compositeson en fait un existing opaque background at the same time that it's rasterized. C'est génial, mais comment puis-je le composer et comment lui donner un arrière-plan opaque et comment le pixelliser?

Le code qu'ils utilisent dans leur exemple de menu Kiosk est le suivant: (C'est OS X, pas iOS, mais je suppose que cela fonctionne!)

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 

Merci!


EDIT: Ajout du code que j'ai réellement utilisé qui a abouti à un texte flou. C'est à partir d'une question connexe que j'ai postée sur l'ajout d'une boîte UILabelplutôt que sur une CATextLayerboîte noire. 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: Voir ma réponse ci-dessous pour savoir comment cela a été résolu. sbg.

Steve
la source
Cela ne répondra pas à votre question, mais peut être pertinent pour quelqu'un comme moi qui cherche uniquement à dessiner du texte attribué, de la manière la plus similaire à l'utilisation d'UILabel: stackoverflow.com/questions/3786528/…
DenNukem

Réponses:

229

Réponse courte - Vous devez définir la mise à l'échelle du contenu:

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

Il y a quelque temps, j'ai appris que lorsque vous avez un code de dessin personnalisé, vous devez vérifier l'affichage de la rétine et mettre à l'échelle vos graphiques en conséquence. UIKits'occupe de la plupart de cela, y compris la mise à l'échelle des polices.

Pas si avec CATextLayer.

Mon flou provenait du fait d'avoir un .zPositionqui n'était pas nul, c'est-à-dire que j'avais une transformation appliquée à mon calque parent. En définissant ceci à zéro, le flou a disparu et a été remplacé par une pixellisation sérieuse.

Après avoir recherché haut et bas, j'ai trouvé que vous pouvez définir .contentsScalepour un CATextLayeret vous pouvez le régler [[UIScreen mainScreen] scale]pour correspondre à la résolution de l'écran. (Je suppose que cela fonctionne pour les non-rétines, mais je n'ai pas vérifié - trop fatigué)

Après avoir inclus cela pour moi, CATextLayerle texte est devenu net. Remarque - ce n'est pas nécessaire pour le calque parent.

Et le flou? Il revient lorsque vous faites une rotation en 3D, mais vous ne le remarquez pas car le texte commence clairement et pendant qu'il est en mouvement, vous ne pouvez pas le dire.

Problème résolu!

Steve
la source
37
C'est la réponse courte: textLayer.contentScale = [[UIScreen mainScreen] scale]; BTW, j'aime aussi les longues réponses :)
nacho4d
Cela s'applique à tout CALayer créé par programme. Par exemple, si vous remarquez que les images dessinées dans votre CALayer ne sont pas nettes sur l'écran Retina, c'est probablement le problème et la solution.
Jeff Argast
17
Correction _textLayer.contentsScale = [[UIScreen mainScreen] scale]; Il vous manque un "s";)
Ralphleon
1
Cela ne fonctionne pas pour moi. J'ajoute un CATextLayer à une vidéo (AVFoundation / AVMutableComposition) par AVVideoCompositionCoreAnimationTool. Le texte a toujours des alias.
vietstone
il n'y a pas d'option d'échelle dans mac osx
Sangram Shivankar
35

Rapide

Définissez le calque de texte pour qu'il utilise la même échelle que l'écran.

textLayer.contentsScale = UIScreen.main.scale

Avant:

entrez la description de l'image ici

Après:

entrez la description de l'image ici

Suragch
la source
Version macOS swift 4.2: (NSScreen.main? .backingScaleFactor)!
roberto
17

Tout d'abord, je voulais souligner que vous avez tagué votre question avec iOS, mais que les gestionnaires de contraintes ne sont disponibles que sur OSX, donc je ne sais pas comment vous faites fonctionner cela à moins que vous n'ayez pu établir un lien avec celui-ci. dans le simulateur en quelque sorte. Sur l'appareil, cette fonctionnalité n'est pas disponible.

Ensuite, je soulignerai simplement que je crée souvent des CATextLayers et que je n'ai jamais le problème de flou auquel vous faites référence, donc je sais que cela peut être fait. En un mot, ce flou se produit parce que vous ne positionnez pas votre calque sur tout le pixel. N'oubliez pas que lorsque vous définissez la position d'un calque, vous utilisez des valeurs flottantes pour les x et y. Si ces valeurs ont des nombres après la décimale, le calque ne sera pas positionné sur tout le pixel et donnera donc cet effet de flou - dont le degré dépend des valeurs réelles. Pour tester cela, créez simplement un CATextLayer et ajoutez-le explicitement à l'arborescence des calques en vous assurant que votre paramètre de position est sur un pixel entier. Par exemple:

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 votre texte est toujours flou, c'est qu'il y a autre chose qui ne va pas. Le texte flou sur votre calque de texte est un artefact de code mal écrit et non une capacité voulue du calque. Lorsque vous ajoutez vos calques à un calque parent, vous pouvez simplement contraindre les valeurs x et y au pixel entier le plus proche et cela devrait résoudre votre problème de flou.

Matt Long
la source
15

Avant de définir shouldRasterize, vous devez:

  1. définir l'échelle de rastérisation de la couche de base que vous allez pixelliser
  2. définir la propriété contentsScale de tous les CATextLayers et éventuellement d'autres types de couches (cela ne fait jamais de mal de le faire)

Si vous ne faites pas # 1, alors la version rétine des sous-calques semblera floue, même pour les CALayers normaux.

- (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
la source
4

Vous devez faire 2 choses, la première a été mentionnée ci-dessus:

Étendez CATextLayer et définissez les propriétés opaque et contentsScale pour prendre en charge correctement l'affichage Retina, puis effectuez le rendu avec l'anti-aliasing activé pour le texte.

+ (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
la source
3

Si vous êtes venu chercher ici un problème similaire pour un CATextLayer sous OSX, après beaucoup de coups de tête au mur, j'ai obtenu le texte clair et net en faisant:

text_layer.contentsScale = self.window!.backingScaleFactor

(J'ai également défini la même échelle que le contenu de la couche d'arrière-plan des vues).

james_alvarez
la source
0

C'est plus rapide et plus facile: il vous suffit de définir le contenu

    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
la source