调整CATextLayer的大小以适合iOS上的文本

时间:2011-06-01 08:07:46

标签: ios4 sizewithfont catextlayer

到目前为止我所有的研究似乎都表明不可能准确地做到这一点。我在一开始就有两个选择:

  

a)使用CATextLayer的布局管理器 - 从iOS 4.0开始不可用

     

b)使用sizeWithFont:constrainedToSize:lineBreakMode:并根据此处返回的大小调整CATextLayer的帧。

选项(b)是最简单的方法,应该有效。毕竟,它与UILabels完美配合。但是当我将相同的帧计算应用于CATextLayer时,框架总是变得比预期或需要的大一点。

事实证明,CATextLayers和UILabels中的行间距(对于相同的字体和大小)是不同的。因此,sizeWithFont(其行间距计算将与UILabels的行间距计算匹配)不会返回CATextLayers的预期大小。

通过使用UILabel打印相同的文本,与CATextLayer相比,并比较结果,进一步证明了这一点。第一行中的文本完全重叠(它是相同的字体),但CATextLayer中的行间距比UILabel略短。 (对不起,我现在无法上传截图,因为我已经包含机密数据,我目前没有时间制作一个示例项目来获取干净的截图。我稍后会为后人上传它们,当时我有时间)

这是一个奇怪的区别,但我认为可以通过为我在那里使用的NSAttributedString指定适当的属性来调整CATextLayer中的间距,但似乎并非如此。查看CFStringAttributes.h我找不到可能与行间距相关的单个属性。

底线:

因此,在需要适合其文本的图层的情况下,似乎无法在iOS上使用CATextLayer。我是对的,还是我错过了什么?

P.S:

  1. 我想使用CATextLayer和NSAttributedString的原因是因为要显示的字符串在不同点上的颜色不同。我想我必须一如既往地回去手工绘制琴弦....当然总是可以选择从sizeWithFont中删除结果以获得正确的行高。

  2. 稍微滥用“代码”标签,以使帖子更具可读性。

  3. 我无法用'CATextLayer'标记帖子 - 令人惊讶的是目前还没有这样的标签。如果有足够声誉的人碰到这篇文章,请相应地标记。

4 个答案:

答案 0 :(得分:17)

试试这个:

- (CGFloat)boundingHeightForWidth:(CGFloat)inWidth withAttributedString:(NSAttributedString *)attributedString {
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFMutableAttributedStringRef) attributedString); 
    CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(inWidth, CGFLOAT_MAX), NULL);
    CFRelease(framesetter);
    return suggestedSize.height;
}

您必须将NSString转换为NSAttributedString。如果是CATextLayer,您可以使用以下CATextLayer子类方法:

- (NSAttributedString *)attributedString {

    // If string is an attributed string
    if ([self.string isKindOfClass:[NSAttributedString class]]) {
        return self.string;
    }

    // Collect required parameters, and construct an attributed string
    NSString *string = self.string;
    CGColorRef color = self.foregroundColor;
    CTFontRef theFont = self.font;
    CTTextAlignment alignment;

    if ([self.alignmentMode isEqualToString:kCAAlignmentLeft]) {
        alignment = kCTLeftTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentRight]) {
        alignment = kCTRightTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentCenter]) {
        alignment = kCTCenterTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentJustified]) {
        alignment = kCTJustifiedTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentNatural]) {
        alignment = kCTNaturalTextAlignment;
    }

    // Process the information to get an attributed string
    CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);

    if (string != nil)
        CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef)string);

    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTForegroundColorAttributeName, color);
    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTFontAttributeName, theFont);

    CTParagraphStyleSetting settings[] = {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment};
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTParagraphStyleAttributeName, paragraphStyle);    
    CFRelease(paragraphStyle);

    NSMutableAttributedString *ret = (NSMutableAttributedString *)attrString;

    return [ret autorelease];
}

HTH。

答案 1 :(得分:2)

我有一个更简单的解决方案,可能适用于您,也可能不适合您。

如果你没有对CATextLayer做任何特别的事情,你不能做UILabel,而是制作一个CALayer并将UILabel的图层添加到CALayer

UILabel*label = [[UILabel alloc]init];

//Do Stuff to label

CALayer *layer = [CALayer layer];

//Set Size/Position

[layer addSublayer:label.layer];

//Do more stuff to layer

答案 2 :(得分:0)

有了LabelKit,您就不再需要CATextLayer。不再使用错误的行距和更宽的字符,所有内容的绘制方式与UILabel相同,同时仍具有动画效果。

答案 3 :(得分:-2)

这个页面足以让我创建一个简单的水平居中CATextLayer:http://lists.apple.com/archives/quartz-dev/2008/Aug/msg00016.html

- (void)drawInContext:(CGContextRef)ctx {
  CGFloat height, fontSize;

  height = self.bounds.size.height;
  fontSize = self.fontSize;

  CGContextSaveGState(ctx);
  CGContextTranslateCTM(ctx, 0.0, (fontSize-height)/2.0 * -1.0);
  [super drawInContext:ctx];
  CGContextRestoreGState(ctx);
}