Ios – Multiline UILabel with adjustsFontSizeToFitWidth

cocoa-touchiosiphoneuikituilabel

I have a multiline UILabel whose font size I'd like to adjust depending on the text length. The whole text should fit into the label's frame without truncating it.

Unfortunately, according to the documentation the adjustsFontSizeToFitWidth property "is effective only when the numberOfLines property is set to 1".

I tried to determine the adjusted font size using

-[NSString (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode]

and then decrementing the font size until it fits. Unfortunately, this method internally truncates the text to fit into the specified size and returns the size of the resulting truncated string.

Best Answer

In this question, 0x90 provides a solution that - although a bit ugly - does what I want. Specifically, it deals correctly with the situation that a single word does not fit the width at the initial font size. I've slightly modified the code so that it works as a category on NSString:

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self sizeWithFont:font constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    UIFont *newFont = font;

    //Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0) {   
        fontSize--;  
        newFont = [UIFont fontWithName:font.fontName size:fontSize];   
        height = [self sizeWithFont:newFont constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
        CGFloat width = [word sizeWithFont:newFont].width;
        while (width > size.width && width != 0) {
            fontSize--;
            newFont = [UIFont fontWithName:font.fontName size:fontSize];   
            width = [word sizeWithFont:newFont].width;
        }
    }
    return fontSize;
}

To use it with a UILabel:

    CGFloat fontSize = [label.text fontSizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:label.frame.size];
    label.font = [UIFont boldSystemFontOfSize:fontSize];

EDIT: Fixed the code to initialize newFont with font. Fixes a crash under certain circumstances.