NSAttributedString 高度受 width 和 numberOfLines 限制 [英] NSAttributedString height limited by width and numberOfLines
问题描述
我需要在不使用 UILabel 的 sizeThatFits 方法的情况下计算自定义标签中的文本矩形.下面的代码无法正常工作.主要思想是在 index = numberOfLines - 1 处找到 CTLine 并返回其最大 y 位置.但结果文本高度有时太大,有时不足以绘制最后一行.
I need to calculate text rect in my custom label not using UILabel's sizeThatFits method. Code below not working correctly. The main idea is find CTLine at index = numberOfLines - 1 and return its max y position. But as a result text height sometimes too large and sometimes not enough to draw last line.
- (CGSize)fittingSizeWithSize:(CGSize)size numberOfLines:(NSInteger)numberOfLines {
if (numberOfLines == 0) {
return [self fittingSizeWithSize:size];
}
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self);
if (framesetter == NULL) {
return CGSizeZero;
}
CGPathRef path = CGPathCreateWithRect(CGRectMake(0,0,size.width,size.height), NULL);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.length), path, NULL);
NSArray *lines = (NSArray *) CTFrameGetLines(frame);
if (lines.count == 0) {
return CGSizeZero;
}
NSUInteger lineIndex = MIN((NSUInteger)numberOfLines, lines.count) - 1;
CTLineRef line = (__bridge CTLineRef) lines[lineIndex];
CGPoint origins[[lines count]];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
CGAffineTransform transform = CGAffineTransformMakeTranslation(0, size.height);
transform = CGAffineTransformScale(transform, 1, -1);
CGRect lineRect;
CGFloat ascent;
CGFloat descent;
lineRect.size.width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, NULL); //8
lineRect.size.height = ascent + descent;
lineRect.origin.y = CGPointApplyAffineTransform(origins[lineIndex], transform).y;
CGFloat height = CGRectGetMaxY(lineRect);
CFRelease(path);
CFRelease(framesetter);
return CGSizeMake(size.width, height);
}
我的 UILabel 子类中使用的 NSAttributedString 类
This category of NSAttributedString used in my UILabel subclass
@implementation SMBDLabel
- (void)drawTextInRect:(CGRect)rect {
if (self.attributedText) {
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.attributedText drawInContext:ctx viewBounds:rect];
} else {
[super drawTextInRect:rect];
}
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
CGSize size = [self.attributedText fittingSizeWithSize:bounds.size numberOfLines:numberOfLines];
return CGRectMake(0, 0, size.width, size.height);
}
- (CGSize)sizeThatFits:(CGSize)size {
return [self.attributedText fittingSizeWithSize:size numberOfLines:self.numberOfLines];
}
@end
我不知道我的错误在哪里.可能在 UILabel 子类中实际上出错
I have no idea where my mistake. Maybe mistake actually in UILabel subclass
推荐答案
解决方案似乎是最简单的.无需获取线条原点和排版边界.带有文本特定文本范围的 CTFramesetterSuggestFrameSizeWithConstraints 将完成所有工作
Solution appears to be simplest. There are no need to get line origins and typographic bound. CTFramesetterSuggestFrameSizeWithConstraints with text specific text range will do all work
- (CGSize)fittingSizeWithSize:(CGSize)size numberOfLines:(NSInteger)numberOfLines {
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self);
if (!framesetter) {
return CGSizeZero;
}
if (numberOfLines == 0) {
CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0,0), NULL, size, NULL);
if (framesetter != NULL) {
CFRelease(framesetter);
}
return CGSizeMake(ceilf(textSize.width), ceilf(textSize.height));
} else {
CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, size.width, CGFLOAT_MAX), NULL);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.length), path, NULL);
if (path != NULL) {
CFRelease(path);
}
NSArray *lines = (NSArray *)CTFrameGetLines(frame);
__block CFIndex len = 0;
[lines enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (numberOfLines > 0 && idx == numberOfLines) {
*stop = YES;
return;
}
CTLineRef line = (__bridge CTLineRef)obj;
CFRange range = CTLineGetStringRange(line);
len += range.length;
}];
CFRange strRange = CFRangeMake(0, len);
CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, strRange, NULL, size, NULL);
if (framesetter != NULL) {
CFRelease(framesetter);
}
return CGSizeMake(ceilf(textSize.width), ceilf(textSize.height));
}
}
这篇关于NSAttributedString 高度受 width 和 numberOfLines 限制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!