NSAttributedString 高度受 width 和 numberOfLines 限制 [英] NSAttributedString height limited by width and numberOfLines

查看:131
本文介绍了NSAttributedString 高度受 width 和 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆