Search code examples
iosobjective-cautolayoutnslayoutconstraintios-autolayout

Wrap UILabel by word and expand UITableViewCell height programmatically


I programmatically created layout constraints to arrange UI components in a table cell.

Here are the constraints I'm implementing:

  • Left part of the cell contains nameUIView (done)
  • Right part of the cell contains valueUIView (done)
  • Widths of the nameUIView and valueUIView part should be equal (done)
  • Height and width nameUIView and valueUIView should be 100% of the available space (done)
  • The nameUIView contains a nameUILabel (done)
  • The nameUILabel should use line wrapping by word (need advice)
  • The UITableViewCell cell should expand in height if the text in the nameUILabel grows too large (need advice)

Layout constraints

How could I achieve line wrapping and height expansion for that layout programmatically?

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        NSString *reuseID = reuseIdentifier;

        // The view containing the label (left, red)
        UIView *nameUIView = [[UIView alloc] init];
        self.nameUIView = attributeNameView;
        [self.nameUIView setBackgroundColor:[UIColor redColor]];
        [self.nameUIView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self.contentView addSubview:self.nameUIView];

        // The label (green)
        UILabel *nameUILabel = [[UILabel alloc] init];
        self.nameUILabel = nameUILabel;
        [self.nameUILabel setTextColor:[UIColor blackColor]];
        [self.nameUILabel setBackgroundColor:[UIColor greenColor]];
        [self.nameUILabel setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self.nameUILabel setLineBreakMode:NSLineBreakByWordWrapping];
        [self.nameUIView addSubview:self.nameUILabel];

        // The view containing the value (right, blue)
        UIView *valueUIView = [[UIView alloc] init];
        self.valueUIView = valueUIView;
        [self.valueUIView setBackgroundColor:[UIColor blueColor]];
        [self.valueUIView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self.contentView addSubview:self.valueUIView];

        NSDictionary *views = NSDictionaryOfVariableBindings(nameUIView, nameUILabel, valueUIView);
        NSArray *constraints;

        // The constraint to align the views horizonzally (1:1) sizing
        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameUIView][valueUIView(==nameUIView)]|"
                                                                       options: 0
                                                                       metrics:nil
                                                                         views:views];

        [self.contentView addConstraints:constraints];

        // 100% height for name view
        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameUIView]|"
                                                              options: 0
                                                              metrics:nil
                                                                views:views];
        [self.contentView addConstraints:constraints];

        NSLayoutConstraint *constraint;

        // Center name label horizontally
        constraint = [NSLayoutConstraint constraintWithItem:nameUILabel
                                attribute:NSLayoutAttributeCenterX
                                relatedBy:NSLayoutRelationEqual
                                toItem:nameUIView
                                attribute:NSLayoutAttributeCenterX
                                multiplier:1.f constant:0.f];// Not possible via VFL
        [self.contentView addConstraint:constraint];

        // Center name label vertically
        constraint = [NSLayoutConstraint constraintWithItem:nameUILabel
                                                                      attribute:NSLayoutAttributeCenterY
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:nameUIView
                                                                      attribute:NSLayoutAttributeCenterY
                                                                     multiplier:1.f constant:0.f];// Not possible via VFL
        [self.contentView addConstraint:constraint];

        // 100% height for value view
        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[valueUIView]|"
                                                              options: 0
                                                              metrics:nil
                                                                views:views];

        [self.contentView addConstraints:constraints];

    }
    return self;
}

Solution

    1. Line wrapping by word

    Adding this additional constraint (set width of label to parent view) did enable word wrapping:

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[attributeNameLabel(==attributeNameView)]|"
                                                          options: 0
                                                          metrics:nil
                                                            views:views];
    
            [self.contentView addConstraints:constraints];
    

    Finally, I had to center the text:

    [self.attributeNameLabel setTextAlignment:NSTextAlignmentCenter];
    
    1. Expanding the cell height based on contents

    It is also needed to set these properties for UITableView

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight =
    

    Set the height of the wrapping superview tame as the label it contains

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameUIView(==nameUILabel)]|"
                                                          options: 0
                                                          metrics:nil
                                                            views:views];
    
    [self.contentView addConstraints:constraints];
    

    Minimum height constraint (optional)

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameUIView(>=50)]|"
                                                          options: 0
                                                          metrics:nil
                                                            views:views];
    
    [self.contentView addConstraints:constraints];