5

Is there anyway I can get the truncated version of the text for a UILabel?

In short, I have a paragraph of text, and two UILabels - label A, which is 2 lines long, and label B, which is a variable height. Label A is above label B. The idea is that label A shows the first two lines of the paragraph of text, and upon a certain user action, label B because visible and displays the rest of the text.

I'm having trouble determining what should go in label B, as I don't know what's being shown in label A. I'd need to also remove the "..." from label A.

Note: I realize this is a bit convoluted but there are some good reasons for it, which I won't clutter up the question with.

Ben Williams
  • 4,695
  • 9
  • 47
  • 72

3 Answers3

12

I wonder if you could use the methods in the NSString UIKit Additions to figure out how much fits into label A.

A crude way might be to start with the first character of your text and test for the size it would take up (-sizeWithFont:forWidth:lineBreakMode: maybe?) and then keep adding characters one at a time until it doesn't fit into your label A any more.

I hope somebody else can come up with a better way to do this, but the above should work.

Update

Last night I looked a bit into Core Text for my own app and came across CTFramesetterSuggestFrameSizeWithConstraints. You could maybe use this to figure out how much of your string fits into the label, by looking at the fitRange in that function.

Update 2:

I think this should work, but I have just typed this in here, so it may not even compile:

UIFont *uiFont = [UIFont systemFontOfZise:13.0f]; // whichever font you're using
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
NSDictionary *attr = [NSDictionary dictionaryWithObject:(id)ctFont forKey:(id)kCTFontAttributeName];
CFRelease(ctfont);
NSAttributedString *attrString  = [[NSAttributedString alloc] initWithString:yourLabelText attributes:attr];
CTFrameSetterRef frameSetter = CTFrameSetterCreateWithAttributedString((CFAttributedStringRef)attrString);
[attrString release];
CFRange fitRange;
CTFrameSetterSuggestFrameSizeWithConstrains(
    frameSetter,
    CFRangeMake(0, 0),
    NULL,
    CGSizeMake(labelWidth, labelHeight),
    &fitRange);
CFRelease(frameSetter);
CFIndex numberOfCharactersThatFit = fitRange.length;
Thomas Müller
  • 15,565
  • 6
  • 41
  • 47
  • Unfortunately the crude option may be the only one. I was hoping for a better solution, but this is at least a solution. I'll leave the question open for a bit longer than accept your answer if nothing better comes in. Thanks. – Ben Williams Jul 19 '11 at 01:14
  • This worked great for me (Update 2), although I had to recreate the CTFont with the kCTFontUIOptimizedTrait bit in order to get the width to closely match what -sizeWithFont: returns. Even then, after about 30 characters, it starts to drift more than 1 pixel. Somehow there's a difference in the kerning, rendering of ligatures, or something else between the UIFont and CTFont. – Seth Kingsley Aug 31 '11 at 17:54
  • Brilliant!! I even tested out on the interface builder. It does get the number of characters close enough how it looks like. – otakuProgrammer Apr 30 '14 at 11:04
  • It returns a wrong value if the text has line breaks *\n*. – Rostyslav Druzhchenko Jan 27 '15 at 16:31
3

thanks to Thomas Müller be sure to set line break mode the myLabel to this:

myLabel.lineBreakMode = NSLineBreakByWordWrapping;

by this method you can get chunked strings that actually fit in the constrained size. Here is the baked code:

- (NSArray *)truncate:(NSString *)text
{
    NSMutableArray *textChunks = [[NSMutableArray alloc] init];

    NSString *chunk = [[NSString alloc] init];

    CTFramesetterRef frameSetter;
    UIFont *uiFont = [UIFont systemFontOfSize:17.0f];
    CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
    NSDictionary *attr = [NSDictionary dictionaryWithObject:(__bridge id)ctFont forKey:(id)kCTFontAttributeName];
    NSMutableAttributedString *attrString  = [[NSMutableAttributedString alloc] initWithString:text attributes:attr];

    CFRange fitRange;
    while (attrString.length>0) {

        frameSetter = CTFramesetterCreateWithAttributedString ((__bridge CFAttributedStringRef) attrString);
            CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0,0), NULL, CGSizeMake(myLabel.frame.size.width, myLabel.frame.size.height), &fitRange);
        CFRelease(frameSetter);

       chunk = [[attrString attributedSubstringFromRange:NSMakeRange(0, fitRange.length)] string];

        [textChunks addObject:chunk];

        [attrString setAttributedString: [attrString attributedSubstringFromRange:NSMakeRange(fitRange.length, attrString.string.length-fitRange.length)]];

    }


    return textChunks;
}
Hashem Aboonajmi
  • 13,077
  • 8
  • 66
  • 75
-5

For Label A, calculate approximate character that should fit perfectly for two lines, for the particular font you are using.

For label B, set variable Height that the whole text must fit into it.

Govind
  • 2,337
  • 33
  • 43
  • No, this is not answering the question. When you assign a long piece of text to a UILabel which falls outside of it's display rect, the text is truncated. I want to know what it is truncated to. – Ben Williams Jul 18 '11 at 05:36