如何获得 CATextLayer 中的文本是清晰的

我制作了一个增加了 CATextLayerCALayer,文字变得模糊不清。在文档中,他们谈到“亚像素反锯齿”,但这对我来说没有太大意义。有人有一个代码片段,使一个 CATextLayer与一个位的文本是明确的?

以下是苹果公司文档中的内容:

注意: CATextLayer 在渲染文本时禁用子像素反走样。 文本只能使用子像素反锯齿绘制时,它是 合成到一个现有的不透明背景,同时 它是光栅化的。没有办法绘制亚像素反锯齿文本 本身,无论是进入一个图像或图层,分别提前 具有背景像素以将文本像素编织成。设置 图层的不透明度属性设置为 YES 不会改变渲染效果 模式。

第二句意味着,如果一个 composites它成为一个 existing opaque background at the same time that it's rasterized.这是伟大的,但我如何合成它,你如何给它一个不透明的背景,你如何光栅化它好看的文本?

他们在 Kiosk Menu 示例中使用的代码是这样的: (它是 OSX,不是 iOS,但我假设它可以工作!)

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

谢谢!


编辑: 添加我实际使用的代码,导致文本模糊。这是从一个相关的问题,我发布了关于添加一个 UILabel而不是一个 CATextLayer,但得到一个黑盒子代替。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);

编辑2: 请看下面我的答案,了解这个问题是如何解决的。

33638 次浏览

First off I wanted to point out that you've tagged your question with iOS, but constraint managers are only available on OSX, so I'm not sure how you're getting this to work unless you've been able to link against it in the simulator somehow. On the device, this functionality is not available.

Next, I'll just point out that I create CATextLayers often and never have the blurring problem you're referring to so I know it can be done. In a nutshell this blurring occurs because you are not positioning your layer on the whole pixel. Remember that when you set the position of a layer, you use a float values for the x and y. If those values have numbers after the decimal, the layer will not be positioned on the whole pixel and will therefore give this blurring effect--the degree of which depending upon the actual values. To test this, just create a CATextLayer and explicitly add it to the layer tree ensuring that your position parameter is on a whole pixel. For example:

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

If your text is still blurry, then there is something else wrong. Blurred text on your text layer is an artifact of incorrectly written code and not an intended capability of the layer. When adding your layers to a parent layer, you can just coerce the x and y values to the nearest whole pixel and it should solve your blurring problem.

Short answer — You need to set the contents scaling:

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

A while ago I learned that when you have custom drawing code, you have to check for the retina display and scale your graphics accordingly. UIKit takes care of most of this, including font scaling.

Not so with CATextLayer.

My blurriness came from having a .zPosition that was not zero, that is, I had a transform applied to my parent layer. By setting this to zero, the blurriness went away, and was replaced by serious pixelation.

After searching high and low, I found that you can set .contentsScale for a CATextLayer and you can set it to [[UIScreen mainScreen] scale] to match the screen resolution. (I assume this works for non-retina, but I haven't checked - too tired)

After including this for my CATextLayer the text became crisp. Note - it's not necessary for the parent layer.

And the blurriness? It comes back when you're rotating in 3D, but you don't notice it because the text starts out clear and while it's in motion, you can't tell.

Problem solved!

Before setting shouldRasterize, you should:

  1. set the rasterizationScale of the base layer you are going to rasterize
  2. set the contentsScale property of any CATextLayers and possibly other types of layers(it never hurts to do it)

If you don't do #1, then the retina version of sub layers will look blurry, even for normal CALayers.

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

You should do 2 things, the first was mentioned above:

Extend CATextLayer and set the opaque and contentsScale properties to properly support retina display, then render with anti aliasing enabled for text.

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

If you came searching here for a similar issue for a CATextLayer in OSX, after much wall head banging, I got the sharp clear text by doing:

text_layer.contentsScale = self.window!.backingScaleFactor

(I also set the views background layer contentsScale to be the same).

This is faster and easier: you just need to set 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];

Swift

Set the text layer to use the same scale as the screen.

textLayer.contentsScale = UIScreen.main.scale

Before:

enter image description here

After:

enter image description here