Search code examples
iosuitableviewautolayoutuilabelnslayoutconstraint

UILabel not showing all text


I have two labels in a UITableViewCell subclass. I want both labels to be multiline labels, so I have set the number of lines to zero for both of the labels. But the problem is that the labels keep getting truncated. For example, I have made the text size of the right label much larger so that it is taller but then I have added a lot of text to the first label on the left. But, rather than keep wrapping and adding more lines, it just truncates when it is the same size as the label on the right.

enter image description here

But the label on the left should have had a lot more text displayed and it should have taken several more lines, but instead it is truncated when it gets to the height of the other label. It is almost like I have a height constraint between the two labels, but I do not.

Here is the code I am using for this. The first part is the table view cell subclass that has the two labels - firstLabel (the one on the left) and secondLabel (the one on the right). Next, I have the code for the view that has the table view and shows how the table view is configured. Finally, there is a UITableView subclass that sets the intrinsicContentSize of the table to be the content size of the table. I added this because without this the table frame always stayed at zero and so the table view data source methods were not getting called. If anyone knows a better way to do this too, that would be much appreciated.

UITableViewCell subclass

This is the implementation for my UITableViewCell. I have vertical content hugging and content compression priorities for both labels, but I have tried this with and without these priorities and the result is the same.

@interface MyTableViewCell ()

@property (strong, nonatomic) UILabel *firstLabel;
@property (strong, nonatomic) UILabel *secondLabel;

@end

@implementation MyTableViewCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    [self.contentView setBackgroundColor:[UIColor systemTealColor]];
    [self.backgroundView setBackgroundColor:[UIColor systemGrayColor]];
    
    [self.contentView addSubview:self.firstLabel];
    [self.contentView addSubview:self.secondLabel];
    
    NSLayoutConstraint *heightConstraint = [self.heightAnchor constraintEqualToConstant:1.0f];
    [heightConstraint setPriority:50];
    
    [NSLayoutConstraint activateConstraints:@[
        [self.firstLabel.leadingAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.leadingAnchor],
        [self.firstLabel.topAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.topAnchor],
        [self.firstLabel.trailingAnchor constraintEqualToAnchor:self.centerXAnchor constant:-4.0f],
        
        [self.secondLabel.leadingAnchor constraintEqualToAnchor:self.centerXAnchor constant:4.0f],
        [self.secondLabel.topAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.topAnchor],
        [self.secondLabel.trailingAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.trailingAnchor],
        
        [self.contentView.layoutMarginsGuide.bottomAnchor constraintGreaterThanOrEqualToAnchor:self.firstLabel.bottomAnchor constant:20.0f],
        [self.contentView.layoutMarginsGuide.bottomAnchor constraintGreaterThanOrEqualToAnchor:self.secondLabel.bottomAnchor constant:20.0f],
        
        heightConstraint
    ]];
}

- (UILabel *)firstLabel {
    if (!self->_firstLabel) {
        self->_firstLabel = [[UILabel alloc] init];
        self->_firstLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self->_firstLabel.numberOfLines = 0;
        self->_firstLabel.userInteractionEnabled = NO;
        self->_firstLabel.contentMode = UIViewContentModeScaleToFill;
        [self->_firstLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
        [self->_firstLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
        [self->_firstLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
        [self->_firstLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
        self->_firstLabel.textAlignment = NSTextAlignmentNatural;
        self->_firstLabel.lineBreakMode = NSLineBreakByTruncatingTail;
        self->_firstLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
        self->_firstLabel.adjustsFontSizeToFitWidth = NO;
        self->_firstLabel.backgroundColor = [UIColor orangeColor];
    }
    return self->_firstLabel;
}

- (UILabel *)secondLabel {
    if (!self->_secondLabel) {
        self->_secondLabel = [[UILabel alloc] init];
        self->_secondLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self->_secondLabel.numberOfLines = 0;
        self->_secondLabel.userInteractionEnabled = NO;
        self->_secondLabel.contentMode = UIViewContentModeScaleToFill;
        [self->_secondLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
        [self->_secondLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
        [self->_secondLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
        [self->_secondLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
        self->_secondLabel.textAlignment = NSTextAlignmentNatural;
        self->_secondLabel.lineBreakMode = NSLineBreakByTruncatingTail;
        self->_secondLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
        self->_secondLabel.adjustsFontSizeToFitWidth = NO;
        self->_secondLabel.backgroundColor = [UIColor yellowColor];
    }
    return self->_secondLabel;
}

- (void)setData:(MyModel *)data {
    self.firstLabel.text = data.first;
    self.secondLabel.text = data.second;
    [self invalidateIntrinsicContentSize];
}

@end

Primary View - has a table view as a child view

This is the actual view that is displayed. As part of the larger application, this view is then displayed in a vertical UIStackView.

This view has a table view as the only subview and the table view is pinned to the edges of this view with AutoLayout. The UITableView is actually an instance of another class called "AutosizingTableView" to get the table view to autosize (without this, the frame of the table stays at zero and the table view data source method, tableView:cellForRowAtIndexPath:, was never being called since the table height was zero. The code for this table view is included after this section.

@interface MyView ()

@property (strong, nonatomic) AutosizingTableView *tableView;

@end

@implementation MyView

- (instancetype)init {
    return [self initWithFrame:CGRectZero];
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        [self initialize];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    [self addSubview:self.tableView];
    
    [NSLayoutConstraint activateConstraints:@[
        [self.tableView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
        [self.tableView.topAnchor constraintEqualToAnchor:self.topAnchor],
        [self.tableView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
        [self.tableView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
        [self.tableView.widthAnchor constraintEqualToAnchor:self.widthAnchor]
    ]];
}

- (UITableView *)tableView {
    if (!self->_tableView) {
        self->_tableView = [[AutosizingTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        self->_tableView.translatesAutoresizingMaskIntoConstraints = NO;
        self->_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        self->_tableView.rowHeight = UITableViewAutomaticDimension;
        self->_tableView.estimatedRowHeight = UITableViewAutomaticDimension;
        self->_tableView.allowsSelection = NO;
        self->_tableView.scrollEnabled = NO;
        self->_tableView.delegate = self;
        self->_tableView.dataSource = self;
        [self->_tableView registerClass:[MyTableViewCell class] forCellReuseIdentifier:@"myTableViewCell"];
    }
    return self->_tableView;
}

- (void)setdata:(NSArray<MyData *> *)data {
    self->_data = data;
    [self.tableView reloadData];
}

#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.data.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myTableViewCell" forIndexPath:indexPath];
    MyData *data = self.data[indexPath.row];
    cell.detail = detail;
    return cell;
}

@end

AutosizingTableView

As was mentioned in the previous section, this is just to make it so the table view autosizes. Without this, the table view height was zero and stayed at zero since the tableView:cellForRowAtIndexPath: method was never getting called because of the table view height.

@implementation AutosizingTableView

- (CGSize)intrinsicContentSize {
    return self.contentSize;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];
    [self invalidateIntrinsicContentSize];
}

@end

Solution

  • A bit difficult to know for certain, because you didn't provide a complete working example, but this may fix your issue.

    See the comments for changes:

    @interface MyTableViewCell ()
    
    @property (strong, nonatomic) UILabel *firstLabel;
    @property (strong, nonatomic) UILabel *secondLabel;
    
    @end
    
    @implementation MyTableViewCell
    
    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
        if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
            [self initialize];
        }
        return self;
    }
    
    - (void)initialize {
        [self.contentView setBackgroundColor:[UIColor systemTealColor]];
        [self.backgroundView setBackgroundColor:[UIColor systemGrayColor]];
        
        [self.contentView addSubview:self.firstLabel];
        [self.contentView addSubview:self.secondLabel];
        
        // not needed
        //NSLayoutConstraint *heightConstraint = [self.heightAnchor constraintEqualToConstant:1.0f];
        //[heightConstraint setPriority:50];
        
        [NSLayoutConstraint activateConstraints:@[
            [self.firstLabel.leadingAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.leadingAnchor],
            [self.firstLabel.topAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.topAnchor],
            [self.firstLabel.trailingAnchor constraintEqualToAnchor:self.centerXAnchor constant:-4.0f],
            
            [self.secondLabel.leadingAnchor constraintEqualToAnchor:self.centerXAnchor constant:4.0f],
            [self.secondLabel.topAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.topAnchor],
            [self.secondLabel.trailingAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.trailingAnchor],
            
            [self.contentView.layoutMarginsGuide.bottomAnchor constraintGreaterThanOrEqualToAnchor:self.firstLabel.bottomAnchor constant:20.0f],
            [self.contentView.layoutMarginsGuide.bottomAnchor constraintGreaterThanOrEqualToAnchor:self.secondLabel.bottomAnchor constant:20.0f],
            
            // not needed
            //heightConstraint
        ]];
    }
    
    - (UILabel *)firstLabel {
        if (!self->_firstLabel) {
            self->_firstLabel = [[UILabel alloc] init];
            self->_firstLabel.translatesAutoresizingMaskIntoConstraints = NO;
            self->_firstLabel.numberOfLines = 0;
            self->_firstLabel.userInteractionEnabled = NO;
            
            // not needed
            //self->_firstLabel.contentMode = UIViewContentModeScaleToFill;
            //[self->_firstLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
            //[self->_firstLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
            //[self->_firstLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
            //[self->_firstLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
            
            self->_firstLabel.textAlignment = NSTextAlignmentNatural;
            
            // word-wrapping, not truncating
            //self->_firstLabel.lineBreakMode = NSLineBreakByTruncatingTail;
            self->_firstLabel.lineBreakMode = NSLineBreakByWordWrapping;
            
            self->_firstLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
            self->_firstLabel.adjustsFontSizeToFitWidth = NO;
            self->_firstLabel.backgroundColor = [UIColor orangeColor];
        }
        return self->_firstLabel;
    }
    
    - (UILabel *)secondLabel {
        if (!self->_secondLabel) {
            self->_secondLabel = [[UILabel alloc] init];
            self->_secondLabel.translatesAutoresizingMaskIntoConstraints = NO;
            self->_secondLabel.numberOfLines = 0;
            self->_secondLabel.userInteractionEnabled = NO;
            
            // not needed
            //self->_secondLabel.contentMode = UIViewContentModeScaleToFill;
            //[self->_secondLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
            //[self->_secondLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
            //[self->_secondLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
            //[self->_secondLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    
            self->_secondLabel.textAlignment = NSTextAlignmentNatural;
            
            // word-wrapping, not truncating
            //self->_secondLabel.lineBreakMode = NSLineBreakByTruncatingTail;
            self->_secondLabel.lineBreakMode = NSLineBreakByWordWrapping;
            
            self->_secondLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
            self->_secondLabel.adjustsFontSizeToFitWidth = NO;
            self->_secondLabel.backgroundColor = [UIColor yellowColor];
        }
        return self->_secondLabel;
    }
    
    - (void)setData:(MyData *)data {
        self.firstLabel.text = data.first;
        self.secondLabel.text = data.second;
        
        // this is needed
        [self.firstLabel layoutIfNeeded];
        [self.secondLabel layoutIfNeeded];
        
        // not needed
        //[self invalidateIntrinsicContentSize];
    }
    
    @end