UILabel and numberOfLines and sizeToFit:

chadbag picture chadbag · Sep 8, 2012 · Viewed 19.3k times · Source

On iOS 5.

I have a UILabel element that is originally placed in a nib. I want the x placement to stay fixed. I want it to take either 1 line, or 2 lines. If more than two lines, it should use the line break setting to show the ellipsis.

I use numberOfLines property and -sizeToFit

If I set the UILabel's numberOfLines property to 0, it will correctly see that for some text, there is not enough room and will wrap it to a second line after calling -sizeToFit but in the rare case that the line is long enough to stretch to 3 lines, I get three lines, which I don't want. If I set numberOfLines property to 2, it actually stretches the whole thing out onto one line and elongates my initial frame set in the nib to be much wider.

 CGRect titleFrame = [[self titleLabel] frame];
 [[self titleLabel] setNumberOfLines:0];
 [[self titleLabel] setText:newProductTitleText];
 [[self titleLabel] sizeToFit];
 CGRect newTitleFrame = [[self titleLabel] frame];

The CGRect are just there for me to be able to calculate things after the fact. So setting numberOfLines to 0 works, and will not change the original origin.x in the frame, and will break long text into multiple lines, but will not constrain it to 2 lines. Setting numberOfLines property to 2, which, when I read the Apple docs

This property controls the maximum number of lines to use in order to fit the label’s text into its bounding rectangle. The default value for this property is 1. To remove any maximum limit, and use as many lines as needed, set the value of this property to 0.

It seems I should be able set this to two and still have it work. I would expect sizeToFit to expand in a positive X and Y direction when expanding to fit all the text but it is expanding in a negative X direction when numberOfLines is set to other than 0.

ETA: the "Autosize" struts are set to upper and left to fix it at a min x,y.

Thanks for any insight.

Answer

Josh Brown picture Josh Brown · Mar 15, 2013

I had a similar problem where -[UILabel sizeToFit] was not respecting the max width I set when numberOfLines was set to 2. Here's how I solved that problem:

    CGFloat titleMaxWidth = 200;
    CGFloat titleMinHeight = 30;
    CGFloat titleMaxHeight = 40;
    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, titleMaxWidth, titleMaxHeight)]; // alternatively, you could do this in a nib
    titleLabel.numberOfLines = 0;
    titleLabel.text = @"The title label will be sized appropriately with this technique.";
    titleLabel.font = [UIFont boldSystemFontOfSize:16];
    [titleLabel sizeToFit];
    titleLabel.numberOfLines = 2;
    if (titleLabel.height > titleMaxHeight)
    {
        titleLabel.height = titleMaxHeight;
    }
    else if (titleLabel.height < titleMinHeight)
    {
        titleLabel.height = titleMinHeight;
    }

As you can see, I also wanted a minimum height for my label, as -sizeToFit often makes the label really small, but you could disregard that code if you don't care about a minimum height. The "magic number" of 40 for the titleMaxHeight comes from experimentation and finding out that a 2 line label with this font really only needs 40px. In this code, -sizeToFit is mainly used to keep the text within the width and determine whether the initial height of 40 can be reduced when we have a short string of text.