带有截断文本的多行NSAttributedString

Joh*_*ght 23 core-text ios

我需要一个带有多行属性文本的UILabel subcass,支持链接,粗体样式等.我还需要带有省略号的尾部截断.支持内部UILabels属性文本开放源代码的无(TTTAttributedLabel,OHAttributedLabel,TTStyledTextLabel)似乎支持为多行文本尾截断.有一个简单的方法来获得这个吗?

Toy*_*dor 38

也许我错过了什么,但有什么不对吗?:

NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"test"];

NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineBreakMode = NSLineBreakByTruncatingTail;
[text addAttribute:NSParagraphStyleAttributeName
                      value:style
                      range:NSMakeRange(0, text.length)];

label.attributedText = text;
Run Code Online (Sandbox Code Playgroud)

这非常有效,并且会在最后添加省略号.

  • 这应该是正确的答案.在iOS6之前,UILabel不支持属性字符串,这就是为什么这曾经是一个难题. (2认同)
  • 您缺少多行部分。当将 `lineBreakMode` 设置为截断时,您本质上是在防止它中断。 (2认同)

Ali*_*are 13

嗨,我是开发人员OHAttributedLabel.

没有简单的方法来实现这一点(正如我在项目的github存储库中打开的相关问题中所解释的那样),因为CoreText不提供此类功能.

执行此操作的唯一方法是使用CoreText对象(CTLine等)自己实现文本布局,而不是使用CTFrameSetter为您执行此操作(但没有管理行截断).我们的想法是建立所有的CTLine,将它们(依赖于NSAttributedString它包含的字形和自动换行策略)一个接一个地放置出来,并自己管理最后的省略号.

我真的很感激,如果有人提出解决方案来做这个工作,因为它似乎需要做一些工作,你必须管理一系列特殊/不寻常的情况(表情符号案例,具有奇怪指标的字体和不寻常的字形,垂直对齐,考虑到省略号本身的大小,知道何时停止).

因此,请随意挖掘并尝试自己实现线条的框架,真的很感激!


小智 9

根据我在这里及以上的网站https://groups.google.com/forum/?fromgroups=#!topic/cocoa-unbound/Qin6gjYj7XU,我提出了以下内容,效果非常好.

- (void)drawString:(CFAttributedStringRef)attString inRect:(CGRect)frameRect inContext:    (CGContextRef)context
{
CGContextSaveGState(context);

// Flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGFloat height = self.frame.size.height;
frameRect.origin.y = (height - frameRect.origin.y)  - frameRect.size.height ;

// Create a path to render text in
// don't set any line break modes, etc, just let the frame draw as many full lines as will fit
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, nil, frameRect);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attString);
CFRange fullStringRange = CFRangeMake(0, CFAttributedStringGetLength(attString));
CTFrameRef aFrame = CTFramesetterCreateFrame(framesetter, fullStringRange, framePath, NULL);
CFRelease(framePath);

CFArrayRef lines = CTFrameGetLines(aFrame);
CFIndex count = CFArrayGetCount(lines);
CGPoint *origins = malloc(sizeof(CGPoint)*count);
CTFrameGetLineOrigins(aFrame, CFRangeMake(0, count), origins);

// note that we only enumerate to count-1 in here-- we draw the last line separately
for (CFIndex i = 0; i < count-1; i++)
{
    // draw each line in the correct position as-is
    CGContextSetTextPosition(context, origins[i].x + frameRect.origin.x, origins[i].y + frameRect.origin.y);
    CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
    CTLineDraw(line, context);
}

// truncate the last line before drawing it
if (count) {
    CGPoint lastOrigin = origins[count-1];
    CTLineRef lastLine = CFArrayGetValueAtIndex(lines, count-1);

    // truncation token is a CTLineRef itself
    CFRange effectiveRange;
    CFDictionaryRef stringAttrs = CFAttributedStringGetAttributes(attString, 0, &effectiveRange);

    CFAttributedStringRef truncationString = CFAttributedStringCreate(NULL, CFSTR("\u2026"), stringAttrs);
    CTLineRef truncationToken = CTLineCreateWithAttributedString(truncationString);
    CFRelease(truncationString);

    // now create the truncated line -- need to grab extra characters from the source string,
    // or else the system will see the line as already fitting within the given width and
    // will not truncate it.

    // range to cover everything from the start of lastLine to the end of the string
    CFRange rng = CFRangeMake(CTLineGetStringRange(lastLine).location, 0);
    rng.length = CFAttributedStringGetLength(attString) - rng.location;

    // substring with that range
    CFAttributedStringRef longString = CFAttributedStringCreateWithSubstring(NULL, attString, rng);
    // line for that string
    CTLineRef longLine = CTLineCreateWithAttributedString(longString);
    CFRelease(longString);

    CTLineRef truncated = CTLineCreateTruncatedLine(longLine, frameRect.size.width, kCTLineTruncationEnd, truncationToken);
    CFRelease(longLine);
    CFRelease(truncationToken);

    // if 'truncated' is NULL, then no truncation was required to fit it
    if (truncated == NULL)
        truncated = (CTLineRef)CFRetain(lastLine);

    // draw it at the same offset as the non-truncated version
    CGContextSetTextPosition(context, lastOrigin.x + frameRect.origin.x, lastOrigin.y + frameRect.origin.y);
    CTLineDraw(truncated, context);
    CFRelease(truncated);
}
free(origins);

CGContextRestoreGState(context);
Run Code Online (Sandbox Code Playgroud)

}

  • 你能解释一下,哪个类是你的子类? (2认同)

gyp*_*Dev 5

这是 @Toydor 答案的 Swift 5 版本:

let attributedString = NSMutableAttributedString(string: "my String")    
let style: NSMutableParagraphStyle = NSMutableParagraphStyle()
style.lineBreakMode = .byTruncatingTail
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle,
                                      value: style,
                                      range: NSMakeRange(0, attributedString.length))
Run Code Online (Sandbox Code Playgroud)