Jak sprawić, by tekst w CATextLayer był przejrzysty

92

Zrobiłem CALayerz dodatkiem CATextLayeri tekst jest rozmyty. W dokumentach mówią o „antyaliasingu subpikselowym”, ale to niewiele dla mnie znaczy. Czy ktoś ma fragment kodu, który tworzy fragment CATextLayertekstu, który jest jasny?

Oto tekst z dokumentacji Apple:

Uwaga: CATextLayer wyłącza antyaliasing podpikselowy podczas renderowania tekstu. Tekst można narysować za pomocą antyaliasingu subpikselowego tylko wtedy, gdy jest on wkomponowany w istniejące nieprzezroczyste tło w tym samym czasie, gdy jest rasteryzowany. Nie ma możliwości samodzielnego narysowania tekstu z antyaliasem subpikselowym, czy to na obrazie, czy w warstwie, osobno, zanim piksele tła zostaną wplecione w piksele tekstu. Ustawienie właściwości krycia warstwy na TAK nie zmienia trybu renderowania.

Drugie zdanie sugeruje, że można uzyskać dobrze wyglądający tekst, jeśli połączymy compositesgo w existing opaque background at the same time that it's rasterized. To świetnie, ale jak go złożyć i jak nadać mu nieprzezroczyste tło i jak go rasteryzować?

Kod, którego używają w swoim przykładzie menu kiosku, jest taki: (To OS X, a nie iOS, ale zakładam, że działa!)

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 

Dzięki!


EDYCJA: Dodanie kodu, którego faktycznie użyłem, spowodowało rozmyty tekst. Jest to z pokrewnego pytania, które opublikowałem, aby dodać UILabelzamiast a, CATextLayerale zamiast tego uzyskać czarne pudełko. 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);

EDYCJA 2: Zobacz moją odpowiedź poniżej, aby dowiedzieć się, jak to zostało rozwiązane. sbg.

Steve
źródło
To nie odpowie na twoje pytanie, ale może być istotne dla kogoś takiego jak ja, który chce tylko narysować przypisany tekst, w sposób najbardziej podobny do używania UILabel: stackoverflow.com/questions/3786528/ ...
DenNukem

Odpowiedzi:

229

Krótka odpowiedź - należy ustawić skalowanie zawartości:

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

Jakiś czas temu dowiedziałem się, że kiedy masz niestandardowy kod rysowania, musisz sprawdzić wyświetlacz siatkówki i odpowiednio skalować swoją grafikę. UIKitzajmuje się większością z tego, łącznie ze skalowaniem czcionek.

Nie tak z CATextLayer.

Moje rozmycie wynikało z posiadania wartości .zPositionróżnej od zera, to znaczy, że na warstwie macierzystej zastosowano transformację. Po ustawieniu tego na zero rozmycie zniknęło i zostało zastąpione poważną pikselacją.

Po wyszukaniu wysokich i niskich stwierdziłem, że możesz ustawić .contentsScalea CATextLayeri możesz ustawić to, [[UIScreen mainScreen] scale]aby pasowało do rozdzielczości ekranu. (Zakładam, że to działa dla innych niż siatkówka, ale nie sprawdziłem - zbyt zmęczony)

Po uwzględnieniu tego dla mojego CATextLayertekstu stał się wyraźny. Uwaga - nie jest to konieczne dla warstwy macierzystej.

A rozmycie? Wraca, gdy obracasz się w 3D, ale nie zauważasz tego, ponieważ tekst zaczyna się wyraźny, a gdy jest w ruchu, nie możesz tego stwierdzić.

Problem rozwiązany!

Steve
źródło
37
Oto krótka odpowiedź: textLayer.contentScale = [[UIScreen mainScreen] scale];
Swoją drogą
Dotyczy to każdego CALayer, który jest tworzony programowo. Na przykład, jeśli zauważysz, że obrazy narysowane w CALayer nie są wyraźne na wyświetlaczu siatkówki, prawdopodobnie jest to problem i rozwiązanie.
Jeff Argast
17
Korekta _textLayer.contentsScale = [[UIScreen mainScreen] scale]; Brakuje "s";)
Ralphleon
1
To nie działa na mnie. Dodaję CATextLayer do wideo (AVFoundation / AVMutableComposition) przez AVVideoCompositionCoreAnimationTool. Tekst nadal ma aliasy.
vietstone,
nie ma opcji skalowania w
systemie
35

Szybki

Ustaw warstwę tekstową tak, aby używała tej samej skali co ekran.

textLayer.contentsScale = UIScreen.main.scale

Przed:

wprowadź opis obrazu tutaj

Po:

wprowadź opis obrazu tutaj

Suragch
źródło
Wersja macOS swift 4.2: (NSScreen.main? .backingScaleFactor)!
roberto
17

Po pierwsze, chciałem zaznaczyć, że oznaczyłeś swoje pytanie tagiem iOS, ale menedżery ograniczeń są dostępne tylko w systemie OSX, więc nie jestem pewien, jak to działa, chyba że udało Ci się połączyć z tym jakoś w symulatorze. Na urządzeniu ta funkcja nie jest dostępna.

Następnie zaznaczę tylko, że często tworzę CATextLayers i nigdy nie mam problemu z rozmyciem, do którego się odnosisz, więc wiem, że można to zrobić. Krótko mówiąc, to rozmycie występuje, ponieważ nie ustawiasz warstwy na całym pikselu. Pamiętaj, że kiedy ustawiasz położenie warstwy, używasz wartości zmiennoprzecinkowych dla x i y. Jeśli wartości te mają liczby po przecinku, warstwa nie zostanie umieszczona na całym pikselu, co spowoduje efekt rozmycia, którego stopień zależy od rzeczywistych wartości. Aby to przetestować, po prostu utwórz CATextLayer i jawnie dodaj ją do drzewa warstw, upewniając się, że parametr pozycji dotyczy całego piksela. Na przykład:

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

Jeśli tekst jest nadal niewyraźny, oznacza to, że coś jest nie tak. Zamazany tekst na warstwie tekstowej jest artefaktem nieprawidłowo napisanego kodu, a nie zamierzoną funkcją warstwy. Dodając warstwy do warstwy nadrzędnej, możesz po prostu przekształcić wartości xiy do najbliższego całego piksela, co powinno rozwiązać problem z rozmyciem.

Matt Long
źródło
15

Przed ustawieniem shouldRasterize należy:

  1. ustaw rasteryzację Skalę warstwy bazowej, którą zamierzasz rasteryzować
  2. ustaw właściwość contentScale dowolnych CATextLayers i ewentualnie innych typów warstw (nigdy nie zaszkodzi)

Jeśli tego nie zrobisz # 1, wersja warstw podrzędnych siatkówki będzie wyglądać na zamazaną, nawet dla normalnych warstw CAL.

- (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
źródło
4

Powinieneś zrobić 2 rzeczy, pierwsza została wspomniana powyżej:

Rozszerz CATextLayer i ustaw właściwości nieprzezroczyste i contentScale, aby poprawnie obsługiwały wyświetlanie siatkówki, a następnie renderuj z włączonym wygładzaniem dla tekstu.

+ (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
źródło
3

Jeśli szukałeś tutaj podobnego problemu dla CATextLayer w OSX, po wielu uderzeniach w głowę, otrzymałem ostry, wyraźny tekst, wykonując:

text_layer.contentsScale = self.window!.backingScaleFactor

(Ustawiłem również zawartość warstwy tła widoków Skala tak, aby była taka sama).

james_alvarez
źródło
0

Jest to szybsze i łatwiejsze: wystarczy ustawić skalę treści

    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
źródło