在 NSStringDrawingTruncatesLastVisibleLine 中使用省略号 (...) 以外的其他内容

Use something else than ellipsis (...) with NSStringDrawingTruncatesLastVisibleLine

我正在尝试使用 [NSAttributedString drawWithRect:options:context:] 方法在后台呈现一些文本,并且我正在传递 (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading| NSStringDrawingTruncatesLastVisibleLine | NSLineBreakByWordWrapping) 作为选项。如果我的字符串长于两行(我已经为此计算了矩形的最大高度),我的文本将被截断 ....

效果很好,但是,我需要使用 ...more 进行截断(最后使用 "more"),而不是 ...

所有渲染都必须在后台线程上完成,因此任何 UI 组件都是不可能的。并且请不要推荐 TTTAttributedLabel,因为我一开始就试图摆脱它,因为它导致我的应用程序滚动性能很差(已经尝试过了)。

在后台线程中绘制字符串时如何使用自定义截断标记?

可能不是最有效的方法,但我最终是这样的:

  • 检查具有所需宽度且没有高度限制的字符串的大小(在 draw 方法中使用 MAXFLOAT 作为边界矩形的高度):

    NSStringDrawingOptions drawingOptions = (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading); [stringToDraw boundingRectWithSize:maximumSize options:drawingOptions context:nil].size;

  • 我知道字体大小,检查结果大小的高度并检查它是否高于预定高度,这表明它是否超过两行。

  • 如果超过两行,使用 [=17] 处答案的修改版本,获取 ...more 大致开始的矩形点处的字符索引=](该点在原文矩形第二行右下角附近某处):

    //this string spans more than two lines.
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];
    // init text container
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:size];
    textContainer.lineFragmentPadding  = 0;
    textContainer.maximumNumberOfLines = 2;
    textContainer.lineBreakMode        = NSLineBreakByClipping;
    [layoutManager addTextContainer:textContainer];
    CGPoint moreStartLocation = CGPointMake(size.width - 60, 30); //35 magic number
    NSUInteger characterIndex = [layoutManager characterIndexForPoint:moreStartLocation
                                                      inTextContainer:textContainer
                             fractionOfDistanceBetweenInsertionPoints:NULL];
    stringToDraw = [attributedString attributedSubstringFromRange:NSMakeRange(0, characterIndex)].mutableCopy;
    [stringToDraw appendAttributedString:self.truncationText];
    size = CGSizeMake(size.width, 35);
    
  • 将原字符串截断为那里的字符(可选:也可以从limit中找到最后一个白色space(例如space,换行符)字符并得到子串从那时起避免剪字)。将“...更多”添加到原始字符串。 (文本可以是任何东西,具有任何属性。只要确保它能在两行中适合结果矩形。我已将其固定为 60px,但也可以获取所需截断字符串的大小,并使用其宽度精确找到最后一个字符)

  • 照常渲染新字符串(以“...more”结尾):

    UIGraphicsBeginImageContextWithOptions(contextSize, YES, 0);
    [stringToDraw drawWithRect:rectForDrawing options:drawingOptions context:nil];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    

这个方法的好处是,因为我们没有接触 UIKit(UIGraphics... 函数和 UIImage 是 thread-safe)我们可以执行整个过程在后台 thread/queue。我正在使用它在背景中预渲染一些带有 attributes/links 等的文本内容,否则在滚动时会在 UI 线程中占用一两帧,并且效果很好。