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
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);
}