Multi-line NSAttributedString with truncated text

后端 未结 9 2037
情书的邮戳
情书的邮戳 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:12

    I used as sample MTLabel. It allows to manage line height. I needed the draw method exactly, so i just put away most stuff i did not need. This method allows me to draw multilined text in rect with tail truncation.

    CGRect CTLineGetTypographicBoundsAsRect(CTLineRef line, CGPoint lineOrigin)
    {
    CGFloat ascent = 0;
    CGFloat descent = 0;
    CGFloat leading = 0;
    CGFloat width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
    CGFloat height = ascent + descent;
    
    return CGRectMake(lineOrigin.x,
                      lineOrigin.y - descent,
                      width,
                      height);
    }
    - (void)drawText:(NSString*) text InRect:(CGRect)rect withFont:(UIFont*)aFont inContext:(CGContextRef)context {
    
    if (!text) {
        return;
    }
    
    BOOL _limitToNumberOfLines = YES;
    int _numberOfLines = 2;
    float _lineHeight = 22;
    
    //Create a CoreText font object with name and size from the UIKit one
    CTFontRef font = CTFontCreateWithName((CFStringRef)aFont.fontName ,
                                          aFont.pointSize,
                                          NULL);
    
    
    //Setup the attributes dictionary with font and color
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                (id)font, (id)kCTFontAttributeName,
                                [UIColor lightGrayColor].CGColor, kCTForegroundColorAttributeName,
                                nil];
    
    NSAttributedString *attributedString = [[[NSAttributedString alloc]
                                             initWithString:text
                                             attributes:attributes] autorelease];
    
    CFRelease(font);
    
    //Create a TypeSetter object with the attributed text created earlier on
    CTTypesetterRef typeSetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    
    //Start drawing from the upper side of view (the context is flipped, so we need to grab the height to do so)
    CGFloat y = self.bounds.origin.y + self.bounds.size.height - rect.origin.y - aFont.ascender;
    
    BOOL shouldDrawAlong = YES;
    int count = 0;
    CFIndex currentIndex = 0;
    
    float _textHeight = 0;
    
    CGContextSaveGState(context);
    
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    
    //Start drawing lines until we run out of text
    while (shouldDrawAlong) {
    
        //Get CoreText to suggest a proper place to place the line break
        CFIndex lineLength = CTTypesetterSuggestLineBreak(typeSetter,
                                                          currentIndex,
                                                          rect.size.width);
    
        //Create a new line with from current index to line-break index
        CFRange lineRange = CFRangeMake(currentIndex, lineLength);
        CTLineRef line = CTTypesetterCreateLine(typeSetter, lineRange);
    
        //Check to see if our index didn't exceed the text, and if should limit to number of lines        
        if (currentIndex + lineLength >= [text length])
        {
            shouldDrawAlong = NO;
        }
        else
        {
            if (!(_limitToNumberOfLines && count < _numberOfLines-1))
            {
                int i = 0;
                if ([[[text substringWithRange:NSMakeRange(currentIndex, lineLength)] stringByAppendingString:@"…"] sizeWithFont:aFont].width > rect.size.width)
                {
                    i--;
                    while ([[[text substringWithRange:NSMakeRange(currentIndex, lineLength + i)] stringByAppendingString:@"…"] sizeWithFont:aFont].width > rect.size.width) 
                    {
                        i--;
                    }
                }
                else
                {
                    i++;
                    while ([[[text substringWithRange:NSMakeRange(currentIndex, lineLength + i)] stringByAppendingString:@"…"] sizeWithFont:aFont].width < rect.size.width) 
                    {
                        i++;
                    }
                    i--;
                }
                attributedString = [[[NSAttributedString alloc] initWithString:[[text substringWithRange:NSMakeRange(currentIndex, lineLength + i)] stringByAppendingString:@"…"] attributes:attributes] autorelease];
    
                CFRelease(typeSetter);
    
                typeSetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    
                CFRelease(line);
    
                CFRange lineRange = CFRangeMake(0, 0);
                line = CTTypesetterCreateLine(typeSetter, lineRange);
    
                shouldDrawAlong = NO;
            }
        }
    
    
        CGFloat x = rect.origin.x;
        //Setup the line position
        CGContextSetTextPosition(context, x, y);
        CTLineDraw(line, context);
    
        count++;
        CFRelease(line);
    
        y -= _lineHeight;
    
        currentIndex += lineLength;
        _textHeight += _lineHeight;
    }
    
    CFRelease(typeSetter);
    
    CGContextRestoreGState(context);
    
    }
    

提交回复
热议问题