web-dev-qa-db-fra.com

Comment faire pour que le texte dans un CATextLayer soit clair

J'ai créé une variable CALayer avec une variable supplémentaire CATextLayer et le texte est flou. Dans la documentation, ils parlent d '"antialiasing sous-pixel", mais cela ne veut pas dire grand chose pour moi. Quelqu'un at-il un extrait de code qui crée une CATextLayer avec un peu de texte clair?

Voici le texte de la documentation d'Apple:

Remarque: CATextLayer désactive l'antialiasing des sous-pixels lors du rendu du texte . Le texte ne peut être dessiné avec un antialiasing inférieur au pixel que s'il est composé dans un fond opaque existant en même temps que c'est pixellisé. Il n’existe 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 avoir les pixels de fond pour tisser les pixels du texte. Réglage la propriété d'opacité du calque sur YES ne modifie pas le rendu mode.

La deuxième phrase implique que l'on peut obtenir un bon texte si l'on composites dans un existing opaque background at the same time that it's rasterized. C'est génial, mais comment puis-je le composer, comment lui donner un fond opaque et comment le rasteriser?

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: L'ajout du code que j'ai réellement utilisé a entraîné un texte flou. C'est à partir d'une question connexe que j'ai posée sur l'ajout d'une UILabel plutôt que d'une CATextLayer mais l'obtention d'une boîte noire à la place. 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.

86
Steve

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 redimensionner vos graphiques en conséquence. UIKit prend en charge la plupart de ces tâches, y compris la mise à l'échelle des polices.

Pas si avec CATextLayer.

Mon flou vient d’un .zPosition qui n’est pas nul, c’est-à-dire qu’une transformation a été appliquée à mon calque parent. En réglant ce paramètre à zéro, le flou a disparu et a été remplacé par une pixellisation sérieuse.

Après une recherche en haut et en bas, j'ai trouvé que vous pouvez définir .contentsScale pour une CATextLayer et le définir sur [[UIScreen mainScreen] scale] pour correspondre à la résolution de l'écran. (Je suppose que cela fonctionne pour les rétiniens, mais je n'ai pas vérifié - trop fatigué)

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

Et le flou? Il revient lorsque vous effectuez une rotation en 3D, mais vous ne le remarquez pas car le texte est clair au début et tant qu'il est en mouvement, vous ne pouvez pas le savoir.

Problème résolu!

213
Steve

Rapide

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

textLayer.contentsScale = UIScreen.main.scale

Avant:

 enter image description here

Après:

 enter image description here

27
Suragch

Tout d'abord, je tiens à souligner que vous avez associé votre question à iOS, mais que les gestionnaires de contraintes ne sont disponibles que sur OSX. Je ne sais donc pas comment vous allez faire en sorte que cela fonctionne à moins que vous n'ayez pu faire le lien avec cela dans le simulateur en quelque sorte. Sur le périphérique, cette fonctionnalité n'est pas disponible.

Ensuite, je soulignerai simplement que je crée souvent CATextLayers sans jamais avoir le problème de flou dont vous parlez, je sais donc que cela peut être fait. En résumé, ce flou est dû au fait que vous ne positionnez pas votre calque sur le pixel entier. N'oubliez pas que lorsque vous définissez la position d'un calque, vous utilisez une valeur flottante pour 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, 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é prévue 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, ce qui devrait résoudre votre problème de flou.

17
Matt Long

Avant de définir devraitRasterize, vous devez:

  1. définir l'échelle de rasterization de la couche de base que vous allez rasteriser
  2. définissez la propriété contentsScale de n'importe quel CATextLayers et éventuellement d'autres types de couches (il n'est jamais inutile de le faire)

Si vous ne le faites pas # 1, la version rétine des sous-couches aura un aspect flou, 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];
}
12
Gabe

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 de la rétine, puis effectuez le rendu avec l'anticrénelage 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];
}
4
MoDJ

Si vous cherchiez ici un problème similaire pour une couche CATextLayer sous OSX, après avoir beaucoup tapé dans la tête du mur, j'ai obtenu le texte clair et net en faisant:

text_layer.contentsScale = self.window!.backingScaleFactor

(J'ai également défini le contenu de la couche d'arrière-plan de vues comme identique). 

1
james_alvarez

C'est plus rapide et plus facile: il vous suffit de définir 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];
0
Alejandro Luengo