4

我在使用 CATextLayer 时遇到了一些问题,这可能是我自己造成的,但我没有找到关于这个主题的任何帮助。我在 OS X 上(在 iOS 上应该是一样的)。

我创建了一个比例因子 > 1 的 CATextLayer 图层,我得到的是一个模糊的文本。我认为,在应用比例之前,图层会被栅格化。这是预期的行为吗?我希望不是,因为它没有任何意义...... CAShapeLayer 在应用它的变换矩阵之后被光栅化,为什么 CATextLayer 应该不同?

万一我做错了什么……那是什么?

CATextLayer *layer = [CATextLayer layer];
layer.string = @"I like what I am doing";
layer.font = (__bridge CFTypeRef)[NSFont systemFontOfSize:24];
layer.fontSize = 24;
layer.anchorPoint = CGPointZero;
layer.frame = CGRectMake(0, 0, 400, 100);
layer.foregroundColor = [NSColor blackColor].CGColor;
layer.transform = CATransform3DMakeScale(2., 2., 1.);
layer.shouldRasterize = NO;
[self.layer addSublayer:layer];

我目前使用的解决方案是将图层的 contentsScale 属性设置为比例因子。问题是这个解决方案不能缩放:如果任何父层的缩放因子发生变化,那么 contentsScale 也应该更新。我应该编写代码来遍历图层树以更新所有 CATextLayers 的 contentsScale 属性......这不完全是我想做的。

另一种解决方案(不是真正的解决方案)是将文本转换为形状并使用 CAShapeLayer。但后来我看不出拥有 CATextLayers 的意义。

CALayer 的自定义子类可以帮助解决这个问题吗?

编辑:甚至 CAGradientLayer 也能够渲染其内容,如 CAShapeLayer,之后应用其变换矩阵。有人可以解释这是怎么可能的吗?

编辑 2:我的猜测是路径和渐变被渲染为 OpenGL 显示列表,因此它们由 OpenGL 本身以屏幕上的实际大小进行光栅化。文本由 Core Animation 光栅化,因此它们是 OpenGL 的位图。

我想我现在会使用 contentsScale 解决方案。也许,在未来,我会将文本转换为形状。为了通过少量工作获得最佳结果,这是我现在使用的代码:

[CATransaction setDisableActions:YES];

CGFloat contentsScale = ceilf(scaleOfParentLayer);
// _scalableTextLayer is a CATextLayer
_scalableTextLayer.contentsScale = contentsScale;
[_scalableTextLayer displayIfNeeded];

[CATransaction setDisableActions:NO];
4

4 回答 4

1

在尝试了所有方法之后,我现在使用的解决方案是 CALayer 的自定义子类。我根本不使用 CATextLayer。

我用这个自定义设置方法覆盖了 contentsScale 属性:

- (void)setContentsScale:(CGFloat)cs
{
    CGFloat scale = MAX(ceilf(cs), 1.); // never less than 1, always integer
    if (scale != self.contentsScale) {
        [super setContentsScale:scale];
        [self setNeedsDisplay];
    }
}

属性的值总是四舍五入到较大的整数值。当舍入值发生变化时,必须重新绘制图层。

我的 CALayer 子类的 display 方法创建了一个文本大小乘以 contentsScale 因子和屏幕比例因子的位图图像。

- (void)display
{
    CGFloat scale = self.contentsScale * [MyUtils screenScale];

    CGFloat width = self.bounds.size.width * scale;
    CGFloat height = self.bounds.size.height * scale;

    CGContextRef bitmapContext = [MyUtils createBitmapContextWithSize:CGSizeMake(width, height)];

    CGContextScaleCTM(bitmapContext, scale, scale);
    CGContextSetShouldSmoothFonts(bitmapContext, 0);

    CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)(_text));

    CGContextSetTextPosition(bitmapContext, 0., self.bounds.size.height-_ascender);
    CTLineDraw(line, bitmapContext);
    CFRelease(line);

    CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
    self.contents = (__bridge id)(image);
    CGImageRelease(image);

    CGContextRelease(bitmapContext);
}

当我更改层次结构根层的比例因子时,我会在所有文本层上循环并将 contentScale 属性设置为相同的因子。只有当比例因子的四舍五入值发生变化时才会调用显示方法(即,如果之前的值为 1.6,现在我设置为 1.7,则没有任何反应。但如果新值为 2.1,则重新显示图层)。

重绘速度方面的成本很小。我的测试是在第 3 代上不断更改 40 个文本层的层次结构的比例因子。平板电脑。它像黄油一样起作用。

于 2013-11-13T11:50:52.720 回答
0

CATextLayer 是不同的,因为底层 CoreText 使用指定的字体大小呈现字形(基于实验的有根据的猜测)。

您可以向父图层添加一个动作,以便一旦它的比例发生变化,它就会改变文本图层的字体大小。

模糊也可能来自未对齐的像素。如果您将文本层置于非整数位置或超层层次结构中的任何转换,则可能会发生这种情况。

或者,您可以继承 CALayer,然后在 drawInContext 中使用 Cocoa 绘制文本:请参见此处的示例: http: //lists.apple.com/archives/Cocoa-dev/2009/Jan/msg02300.html http://people.omnigroup。 com/bungi/TextDrawing-20090129.zip

于 2013-05-03T12:46:13.630 回答
0

如果您想拥有 CAShapeLayer 的确切行为,那么您需要将您的字符串转换为贝塞尔路径并让 CAShapeLayer 呈现它。这是一项工作,但是您将获得您正在寻找的确切行为。另一种方法是缩放字体大小。这每次都会产生清晰的文本,但它可能不适合您的确切情况。

于 2013-05-03T14:23:44.647 回答
0

要将文本绘制为 CAShapeLayer,请查看 Apple 示例代码“CoreAnimationText”:http: //developer.apple.com/library/mac/#samplecode/CoreAnimationText/Listings/Readme_txt.html

于 2013-05-06T10:26:45.823 回答