http://stackoverflow.com/questions/7611816/multi-line-nsattributedstring-with-truncated-text/10172794#10172794
|
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 code that supports attributed text inside UILabels (TTTAttributedLabel , OHAttribuedLabel , TTStyledTextLabel ) seem to support tail truncation for multi-line text. Is there an easy way to get this?
|
|
asked Sep 30 '11 at 14:27
|
|
|
|
|
Hi I am the developer of OHAttributedLabel .
There is no easy way to achieve this (as explained in the associated issue I opened on the github repository of my project), because CoreText does not offer such feature.
The only way to do this would be to implement the text layout yourself using CoreText objects (CTLine, etc) instead of using the CTFrameSetter that does this for you (but w/o managing line truncation). The idea would be to build all the CTLines to lay them out (depending on the glyphs in your NSAttributedString it contains and the word wrapping policy) one after the other and manage the ellipsis at the end yourself.
I would really appreciate if someone propose a solution to do this propery as it seems a bit of work to do and you have to manage a range of special/unusual cases too (emoji cases, fonts with odd metrics and unusual glyphs, vertical alignment, take into account the size of the ellipsis itself at the end to know when to stop).
So feel free to dig around and try to implement the framing of the lines yourself it would be really appreciated!
|
|
answered Sep 30 '11 at 14:49
|
|
|
|


|
Based on what I found here and over at https://groups.google.com/forum/?fromgroups=#!topic/cocoa-unbound/Qin6gjYj7XU, I came up with the following which works very well.
- (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
|
CTTypesetterSuggestLineBreak
function which will suggest a line break on an attributed string for a given width. This width could be calculated on the last CTLine by calculating the width of the ellipsis with the same attributed string and it seems like most of your above special cases would then be handled properly. – John Wright Oct 6 '11 at 11:19