Подтвердить что ты не робот

Многострочный NSAttributedString с усеченным текстом

Мне нужна субкасса UILabel с многострочным атрибутом текста с поддержкой ссылок, жирным стилем и т.д. Мне также нужно усечь хвост с помощью многоточия. Ни один из открытого исходного кода, который поддерживает атрибутный текст внутри UILabels (TTTAttributedLabel, OHAttributedLabel, TTStyledTextLabel), похоже, поддерживает усечение хвоста для многострочного текста. Есть ли простой способ получить это?

4b9b3361

Ответ 1

Привет Я разработчик OHAttributedLabel.

Нет простого способа добиться этого (как объясняется в связанной проблеме, которую я открыл в репозитории github моего проекта), потому что CoreText не предлагает такую ​​функцию.

Единственный способ сделать это - реализовать сам макет текста с использованием объектов CoreText (CTLine и т.д.) вместо использования CTFrameSetter, который сделает это для вас (но без управления усечением строк). Идея состояла бы в том, чтобы создать все CTLines, чтобы выложить их (в зависимости от глифов в вашем NSAttributedString он содержит и политику переноса слов) один за другим и управлять эллипсисом в конце самостоятельно.

Я был бы очень признателен, если бы кто-то предложил решение, чтобы сделать это, как это кажется немного работы, и вы должны управлять рядом специальных/необычных случаев тоже (примеры эможи, шрифты с нечетными метриками и необычными глифами, вертикальное выравнивание, учитывайте размер самого многоточия в конце, чтобы знать, когда остановиться).

Так что не стесняйтесь копаться и пытаться реализовать обрамление линий самим, это было бы действительно оценено!

Ответ 2

Может, я что-то упустил, но что не так?

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;

Это отлично работает и добавит эллипсис к концу.

Ответ 3

Основываясь на том, что я нашел здесь и в 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);

}

Ответ 4

Я не пробовал это во всех случаях, но что-то вроде этого может работать для усечения:

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

Ответ 5

Вы можете использовать следующий код, чтобы иметь более простое решение.

    // last line.
    if (_limitToNumberOfLines && count == _numberOfLines-1)
    {
        // check if we reach end of text.
        if (lineRange.location + lineRange.length < [_text length])
        {
            CFDictionaryRef dict = ( CFDictionaryRef)attributes;
            CFAttributedStringRef truncatedString = CFAttributedStringCreate(NULL, CFSTR("\u2026"), dict);

            CTLineRef token = CTLineCreateWithAttributedString(truncatedString);

            // not possible to display all text, add tail ellipsis.
            CTLineRef truncatedLine = CTLineCreateTruncatedLine(line, self.bounds.size.width - 20, kCTLineTruncationEnd, token);
            CFRelease(line); line = nil;
            line = truncatedLine;
        }
    }

Я использую MTLabel в своем проекте, и это действительно приятное решение для моего проекта.

Ответ 6

Многолинейный вертикальный глиф с усеченным. Swift3 и Swift4.
Добавить: совместимость с Xcode9.1 Swift4. (используйте блок "#if swift ( >= 4.0)" )

class MultiLineVerticalGlyphWithTruncated: UIView, SimpleVerticalGlyphViewProtocol {

    var text:String!
    var font:UIFont!
    var isVertical:Bool!

    func setupProperties(text: String?, font:UIFont?, isVertical:Bool) {
        self.text = text ?? ""
        self.font = font ?? UIFont.systemFont(ofSize: UIFont.systemFontSize)
        self.isVertical = isVertical
    }


    override func draw(_ rect: CGRect) {
        if self.text == nil {
            return
        }

        // Create NSMutableAttributedString
        let attributed = NSMutableAttributedString(string: text) // if no ruby
        //let attributed = text.attributedStringWithRuby() // if with ruby, Please create custom method

    #if swift(>=4.0)
        attributed.addAttributes([
            NSAttributedStringKey.font: font,
            NSAttributedStringKey.verticalGlyphForm: isVertical,
            ],
                                 range: NSMakeRange(0, attributed.length))
    #else
        attributed.addAttributes([
            kCTFontAttributeName as String: font,
            kCTVerticalFormsAttributeName as String: isVertical,
            ],
                                 range: NSMakeRange(0, attributed.length))
    #endif

        drawContext(attributed, textDrawRect: rect, isVertical: isVertical)
    }
}

protocol SimpleVerticalGlyphViewProtocol {
}

extension SimpleVerticalGlyphViewProtocol {

    func drawContext(_ attributed:NSMutableAttributedString, textDrawRect:CGRect, isVertical:Bool) {

        guard let context = UIGraphicsGetCurrentContext() else { return }

        var path:CGPath
        if isVertical {
            context.rotate(by: .pi / 2)
            context.scaleBy(x: 1.0, y: -1.0)
            path = CGPath(rect: CGRect(x: textDrawRect.origin.y, y: textDrawRect.origin.x, width: textDrawRect.height, height: textDrawRect.width), transform: nil)
        }
        else {
            context.textMatrix = CGAffineTransform.identity
            context.translateBy(x: 0, y: textDrawRect.height)
            context.scaleBy(x: 1.0, y: -1.0)
            path = CGPath(rect: textDrawRect, transform: nil)
        }

        let framesetter = CTFramesetterCreateWithAttributedString(attributed)
        let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, nil)

        // Check need for truncate tail
        if (CTFrameGetVisibleStringRange(frame).length as Int) < attributed.length {

            // Required truncate

            let linesNS: NSArray  = CTFrameGetLines(frame)
            let linesAO: [AnyObject] = linesNS as [AnyObject]
            var lines: [CTLine] = linesAO as! [CTLine]

            let boundingBoxOfPath = path.boundingBoxOfPath


            let lastCTLine = lines.removeLast()

            let truncateString:CFAttributedString = CFAttributedStringCreate(nil, "\u{2026}" as CFString, CTFrameGetFrameAttributes(frame))
            let truncateToken:CTLine = CTLineCreateWithAttributedString(truncateString)

            let lineWidth = CTLineGetTypographicBounds(lastCTLine, nil, nil, nil)
            let tokenWidth = CTLineGetTypographicBounds(truncateToken, nil, nil, nil)
            let widthTruncationBegins = lineWidth - tokenWidth
            if let truncatedLine = CTLineCreateTruncatedLine(lastCTLine, widthTruncationBegins, .end, truncateToken) {
                lines.append(truncatedLine)
            }

            var lineOrigins = Array<CGPoint>(repeating: CGPoint.zero, count: lines.count)
            CTFrameGetLineOrigins(frame, CFRange(location: 0, length: lines.count), &lineOrigins)
            for (index, line) in lines.enumerated() {
                context.textPosition = CGPoint(x: lineOrigins[index].x + boundingBoxOfPath.origin.x, y:lineOrigins[index].y + boundingBoxOfPath.origin.y)
                CTLineDraw(line, context)
            }

        }
        else {
            // Not required truncate
            CTFrameDraw(frame, context)
        }
    }
}

Как использовать

class ViewController: UIViewController {

    @IBOutlet weak var multiLineVerticalGlyphWithTruncated: MultiLineVerticalGlyphWithTruncated! // UIView

    let font:UIFont = UIFont(name: "HiraMinProN-W3", size: 17.0) ?? UIFont.systemFont(ofSize: 17.0)

    override func viewDidLoad() {
        super.viewDidLoad()

        let text = "iOS 11 sets a new standard for what is already the world’s most advanced mobile operating system. It makes iPhone better than before. It makes iPad more capable than ever. And now it opens up both to amazing possibilities for augmented reality in games and apps. With iOS 11, iPhone and iPad are the most powerful, personal, and intelligent devices they’ve ever been."
        // If check for Japanese
//        let text = "すでに世界で最も先進的なモバイルオペレーティングシステムであるiOSに、iOS 11が新たな基準を打ち立てます。iPhoneは今まで以上に優れたものになり、iPadはかつてないほどの能力を手に入れます。さらにこれからはどちらのデバイスにも、ゲームやアプリケーションの拡張現実のための驚くような可能性が広がります。iOS 11を搭載するiPhoneとiPadは、間違いなくこれまでで最もパワフルで、最もパーソナルで、最も賢いデバイスです。"

        // if not vertical text, isVertical = false
        multiLineVerticalGlyphWithTruncated.setupProperties(text: text, font: font, isVertical: true)
    }
}

Ответ 7

Я интегрировал решение wbyoung в OHAttributedLabel drawTextInRect: метод, если кому-то интересно:

- (void)drawTextInRect:(CGRect)aRect
{
    if (_attributedText)
    {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSaveGState(ctx);

    // flipping the context to draw core text
    // no need to flip our typographical bounds from now on
    CGContextConcatCTM(ctx, CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f));

    if (self.shadowColor)
    {
        CGContextSetShadowWithColor(ctx, self.shadowOffset, 0.0, self.shadowColor.CGColor);
    }

    [self recomputeLinksInTextIfNeeded];
    NSAttributedString* attributedStringToDisplay = _attributedTextWithLinks;
    if (self.highlighted && self.highlightedTextColor != nil)
    {
        NSMutableAttributedString* mutAS = [attributedStringToDisplay mutableCopy];
        [mutAS setTextColor:self.highlightedTextColor];
        attributedStringToDisplay = mutAS;
        (void)MRC_AUTORELEASE(mutAS);
    }
    if (textFrame == NULL)
    {
        CFAttributedStringRef cfAttrStrWithLinks = (BRIDGE_CAST CFAttributedStringRef)attributedStringToDisplay;
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(cfAttrStrWithLinks);
        drawingRect = self.bounds;
        if (self.centerVertically || self.extendBottomToFit)
        {
            CGSize sz = CTFramesetterSuggestFrameSizeWithConstraints(framesetter,CFRangeMake(0,0),NULL,CGSizeMake(drawingRect.size.width,CGFLOAT_MAX),NULL);
            if (self.extendBottomToFit)
            {
                CGFloat delta = MAX(0.f , ceilf(sz.height - drawingRect.size.height))+ 10 /* Security margin */;
                drawingRect.origin.y -= delta;
                drawingRect.size.height += delta;
            }
            if (self.centerVertically) {
                drawingRect.origin.y -= (drawingRect.size.height - sz.height)/2;
            }
        }
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, drawingRect);
        CFRange fullStringRange = CFRangeMake(0, CFAttributedStringGetLength(cfAttrStrWithLinks));
        textFrame = CTFramesetterCreateFrame(framesetter,fullStringRange, path, NULL);
        CGPathRelease(path);
        CFRelease(framesetter);
    }

    // draw highlights for activeLink
    if (_activeLink)
    {
        [self drawActiveLinkHighlightForRect:drawingRect];
    }

    BOOL hasLinkFillColorSelector = [self.delegate respondsToSelector:@selector(attributedLabel:fillColorForLink:underlineStyle:)];
    if (hasLinkFillColorSelector) {
        [self drawInactiveLinkHighlightForRect:drawingRect];
    }

    if (self.truncLastLine) {
        CFArrayRef lines = CTFrameGetLines(textFrame);
        CFIndex count = MIN(CFArrayGetCount(lines),floor(self.size.height/self.font.lineHeight));

        CGPoint *origins = malloc(sizeof(CGPoint)*count);
        CTFrameGetLineOrigins(textFrame, 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(ctx, origins[i].x + drawingRect.origin.x, origins[i].y + drawingRect.origin.y);
            CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
            CTLineDraw(line, ctx);
        }

        // 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((BRIDGE_CAST CFAttributedStringRef)_attributedTextWithLinks, 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((BRIDGE_CAST CFAttributedStringRef)_attributedTextWithLinks) - rng.location;

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

            CTLineRef truncated = CTLineCreateTruncatedLine(longLine, drawingRect.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(ctx, lastOrigin.x + drawingRect.origin.x, lastOrigin.y + drawingRect.origin.y);
            CTLineDraw(truncated, ctx);
            CFRelease(truncated);
        }
        free(origins);
        }
         else{
            CTFrameDraw(textFrame, ctx);
         }

        CGContextRestoreGState(ctx);
    } else {
        [super drawTextInRect:aRect];
        }
}

Ответ 8

Вот версия Swift 5 ответа @Toydor:

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

Ответ 9

Я использовал в качестве образца MTLabel. Он позволяет управлять высотой линии. Мне нужен был метод рисования точно, поэтому я просто убрал большинство вещей, которые мне не нужны. Этот метод позволяет мне рисовать многострочный текст в прямоугольнике с усечением хвоста.

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

}