Search code examples
iosobjective-cuilabelsizetofit

How to programmatically sizeToFit width AND height on UILabel?


I'm programmatically creating multi-line UILabels ([label setNumberOfLines:0];).

The built-in sizeToFit method of UILabel works great for 1 line UILabels, but for multi-line text, it sets the height properly, but the width is set too small, causing longer text lines to wrap.

I don't know the label width until after the user enters their text. I want to resize the labels to fit the width of the longest line of text. And per @DonMag's comment, I also want to restrict the label to not be wider than the screen.

I tried different lineBreakMode settings but there isn't a 'nowrap' option.

I've searched SO and there are many related solutions but none that solve the problem of sizeToFit for both width and height.

Is there a way to programmatically size a multi-line UILabel to fit BOTH the width AND the height of the text?


Solution

  • You can do this with boundingRectWithSize...

    Add your label to the view and give it a starting width constraint (doesn't really matter what value, as it will be changed).

    Keep a reference to that width constraint (IBOutlet works fine if you're using IB).

    Don't give it a height constraint.

    When you set the text of the label, you can use this to change its width:

    // get the font of the label
    UIFont *theFont = _theLabel.font;
    
    // get the text of the label
    NSString *theString = _theLabel.text;
    
    // calculate the bounding rect, limiting the width to the width of the view
    CGRect r = [theString boundingRectWithSize:CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX)
                                       options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                    attributes:@{NSFontAttributeName: theFont}
                                       context:nil];
    
    // change the constant of the constraint to the calculated width
    _theWidthConstraint.constant = ceil(r.size.width);
    
    // NOTE: If you are *not* using auto-layout, 
    // this same calculation can be used to explicitly set
    // the .frame of the label.
    

    Edit:

    As per the OP's requirement, a complete, runnable example -- using code only, no storyboards / IB -- can be found here: https://github.com/DonMag/MultilineLabelFitWidth

    Edit 2:

    GitHub project updated... now includes examples for both manual frame setting and auto layout / constraints.