Multi-line NSAttributedString with truncated text

后端 未结 9 2021
情书的邮戳
情书的邮戳 2020-12-23 14:55

I need a UILabel subcass with multiline attributed text with support for links, bold styles, etc. I also need tail truncation with an ellipsis. None of the open source co

9条回答
  •  [愿得一人]
    2020-12-23 15:22

    I haven't tried this in all cases, but something like this could work for truncation:

    NSAttributedString *string = self.attributedString;
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    
    CFAttributedStringRef attributedString = (__bridge CFTypeRef)string;
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedString);
    CGPathRef path = CGPathCreateWithRect(self.bounds, NULL);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
    
    BOOL needsTruncation = CTFrameGetVisibleStringRange(frame).length < string.length;
    CFArrayRef lines = CTFrameGetLines(frame);
    NSUInteger lineCount = CFArrayGetCount(lines);
    CGPoint *origins = malloc(sizeof(CGPoint) * lineCount);
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
    
    for (NSUInteger i = 0; i < lineCount; i++) {
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        CGPoint point = origins[i];
        CGContextSetTextPosition(context, point.x, point.y);
    
        BOOL truncate = (needsTruncation && (i == lineCount - 1));
        if (!truncate) {
            CTLineDraw(line, context);
        }
        else {
            NSDictionary *attributes = [string attributesAtIndex:string.length-1 effectiveRange:NULL];
            NSAttributedString *token = [[NSAttributedString alloc] initWithString:@"\u2026" attributes:attributes];
            CFAttributedStringRef tokenRef = (__bridge CFAttributedStringRef)token;
            CTLineRef truncationToken = CTLineCreateWithAttributedString(tokenRef);
            double width = CTLineGetTypographicBounds(line, NULL, NULL, NULL) - CTLineGetTrailingWhitespaceWidth(line);
            CTLineRef truncatedLine = CTLineCreateTruncatedLine(line, width-1, kCTLineTruncationEnd, truncationToken);
    
            if (truncatedLine) { CTLineDraw(truncatedLine, context); }
            else { CTLineDraw(line, context); }
    
            if (truncationToken) { CFRelease(truncationToken); }
            if (truncatedLine) { CFRelease(truncatedLine); }
        }
    }
    
    free(origins);
    CGPathRelease(path);
    CFRelease(frame);
    CFRelease(framesetter);
    

提交回复
热议问题