iosuitableviewnsstringuilabelsizewithfont

NSString sizeWithFont:constrainedToSize: computing wrong height


I have a Subtitle style UITableViewCell which height changes dynamically depending on the length of the text for each field. The problem is that the textLabel's height (CGSize size) does not increase if the label has multiple lines.

http://s10.postimg.org/6urher7s9/text_Label.png

The weird part is that the detailTextLabel's height is increasing as it should (CGSize size2). The code to calculate both heights are identical.

http://s21.postimg.org/m0af4gyw7/detail.png

Here is my function:

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    SETLISTFMNS0Song *song = [[[selectedSetlist.sets objectAtIndex:indexPath.section] songs]objectAtIndex:indexPath.row];
    CGSize size = [song.name sizeWithFont:[UIFont fontWithName:setlistFont size:labelFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX)];
    NSLog(@"Label: \"%@\" \tLabel Size: %f W %f H", song.name, size.width, size.height);

    NSMutableString *detail;
    if ([song cover]) {
        detail = [[NSMutableString alloc] initWithFormat:@"(%@ cover)", [[song cover] name]];
    }
    if ([song with]) {
        if (!detail) {
            detail = [[NSMutableString alloc] initWithFormat:@"(with %@)", [[song with] name]];
        }
        else {
            [detail appendFormat:@" (with %@)", [[song with] name]];
        }
    }
    if ([song info]) {
        if (!detail) {
            detail = [[NSMutableString alloc] initWithFormat:@"(%@)", [song info]];
        }
        else {
            [detail appendFormat:@" (%@)", [song info]];
        }
    }

    if (detail.length != 0) {
        CGSize size2 = [detail sizeWithFont:[UIFont fontWithName:setlistFont size:detailFontSize] constrainedToSize:CGSizeMake(self.setsTable.bounds.size.width, CGFLOAT_MAX)];
        size.height += size2.height;
        NSLog(@"Detail Label: \"%@\" \tDetail Label Size: %f W %f H", detail, size2.width, size2.height);
    }

    return size.height + 5;
}

I am also setting both textLabel's numberOfLines property to 0 in cellForRowAtIndexPath to support multiple lines:

cell.textLabel.numberOfLines = 0;
cell.detailTextLabel.numberOfLines = 0;

UPDATE: Thanks to @josh I now understand why this is happening. I had the width constraints set to the width of the UITableView, which is too wide. Anyone know how to find the width of the UILabel before it is created? HA!

Thanks!


Solution

  • How long are the strings you're calculating the size against? I ask because you're sizeWithFont:constrainedToSize: function is constraining to the full width of the cell, but your labels are probably not the full width of the cell.

    What I suspect is happening is song.info is just short enough that it would fit on one line if the line were the full width of the cell, and that your song detail is long enough that it is calculating the correct number of lines, but not so long as to exceed the calculated height.

    All of that to say, I think what you need to do is find out the widths of textLabel and detailTextLabel and set your constraints to those values.

    Update - A way of calculating label widths

    Since the width of the labels inside of a cell are dependent on the width of the cell, and since cell's aren't created at the time heightForRowAtIndexPath: is called, we need to come up with a way to know the labels' widths before they the widths are set. The only way to do this is to set the widths ourselves. Here's how I would do it:

    MyCell.h

    #import <UIKit/UIKit.h>
    
    @interface MyCell : UITableViewCell
    
    + (CGFloat)textLabelWidthForCellOfWidth:(CGFloat)cellWidth;
    
    @end
    

    MyCell.m

    #import "MyCell.h"
    
    @implementation MyCell
    
    - (void)layoutSubviews {
        [super layoutSubviews];
    
        CGRect frame = self.textLabel.frame;
        frame.size.width = [MyCell textLabelWidthForCellOfWidth:self.frame.size.width];
        self.textLabel.frame = frame;
    }
    
    + (CGFloat)textLabelWidthForCellOfWidth:(CGFloat)cellWidth {
        // This calculation can be as complex as necessary to account for all elements that affect the label
        return cellWidth - 20;
    }
    
    @end
    

    Then in your heightForRowAtIndexPath: implementation you can call the same class method:

    CGFloat labelWidth = [MyCell textLabelWidthForCellOfWidth:self.tableView.frame.size.width]; // Since non-grouped cells are the full width of the tableView
    CGSize size2 = [detail sizeWithFont:[UIFont fontWithName:setlistFont size:detailFontSize] constrainedToSize:CGSizeMake(labelWidth, CGFLOAT_MAX)];
    

    You would create a separate Class Method (+) for each label that you need to reference.