到目前为止我所有的研究似乎都表明不可能准确地做到这一点。我在一开始就有两个选择:
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:
我想使用CATextLayer和NSAttributedString的原因是因为要显示的字符串在不同点上的颜色不同。我想我必须一如既往地回去手工绘制琴弦....当然总是可以选择从sizeWithFont中删除结果以获得正确的行高。
稍微滥用“代码”标签,以使帖子更具可读性。
我无法用'CATextLayer'标记帖子 - 令人惊讶的是目前还没有这样的标签。如果有足够声誉的人碰到这篇文章,请相应地标记。
答案 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);
}