UITableViewCell with left and right labels. How to make them display correctly?

Viewed 1768

I have an UITableViewCell with 2 labels which can have different content. Sometimes the left label is very big and the right label is small, or empty or sometimes the right label contains a lot of information.

Is it possible to make them display correctly (i.e. no label should be truncated and the hight of the labels should be as small as possible) only by playing with the constraints and content hugging/compression resistance priorities?

I already tried adding constraints for minimum width, or changing the priorities for compression and hugging to 1000, but I always have some issues like either the text is truncated (see screenshot) or one of the labels is displayed on 10 lines and the other on only one line (see the second screenshot).

Here is some sample data that I'm playing with (demo project available here https://github.com/adi2004/iosamples/tree/master/TableView):

    let data = [
    (left: "left one two three four five", right: "7"),
    (left: "left one two three four five 6 7 more here", right: "right one two three four five 6 7"),
    (left: "left one two three four five 6 7", right: "right one two three four five 6 7 something"),
    (left: "6 = ", right: "right one two three four five 6 7"),
    (left: "left one two three four five 6 7 right one two three four five 6 7 eight right one two three four five 6 7 eight", right: "")
]

Here are some samples of the issues I'm facing: Imgur

-- or --

Imgur

3 Answers

First of all check this screenshot. Is it fulfilling what you want?

enter image description here

From your comments on other questions, i can say, this one is what you were looking for.

These are steps that i have taken for creating the cell for this:

  1. Add TableViewCell in your tableview, give identifier and cell class to it.
  2. Add UIStackview in this cell with Alignment = Fill, Distribution = Fill Proportionally, Spacing = 0 . Give top, bottom, leading, trailing constraint to stackview, currently I used value = 12.(use whatever marging you want)
  3. Add 2 UIViews inside this stackview, add background to both view, rightnow, i gave green and red to each one.
  4. Now add UILabel to each UIView, set numberOfLines = 0, give top, bottom, leading and trailing constraints with value 0 to each label. Now, just connect the IBOutlets to respective cell class.

Your required cell is ready, set value you want in datasource and run the code. Let me know any further help you required. Make sure you are adding UILabel inside UIView, not directly to UIStackview. This is done because, multiline label inside stackview will never give you proper result.

UPDATE:

As @Gal pointd out that above code is not working while dequing.. Here, is changes that i made to resolve the same.

Change 1 : Updated cellForRowAtIndexPath to invalidate intrinsicContentSize of containerViews and asking cell to layout the constraints again. Here, is my implementation:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell") as! TestCell;
    cell.leftLabel.text = data[indexPath.row].left;
    cell.rightLabel.text = data[indexPath.row].right;
    cell.leftLabel.superview!.invalidateIntrinsicContentSize();
    cell.rightLabel.superview!.invalidateIntrinsicContentSize();
    cell.layoutIfNeeded()
    return cell;
}

Change 2: Making above single change were working for all the cases, except that one in which any label's text value was empty(in that uiview was apparently removed out from stackview, by stackview itself). So, to prevent this, i have again updated my above cellForRowAtIndexPath code, making sure that any label's text is not getting empty. If empty, then adding just empty space there. It will be like this,

let leftText = data[indexPath.row].left;
if leftText.isEmpty{
    cell.leftLabel.text = " "
}else{
    cell.leftLabel.text = leftText;
}

let rightText = data[indexPath.row].right;
if rightText.isEmpty{
    cell.rightLabel.text = " "
}else{
    cell.rightLabel.text = rightText
} 

After making above change. The screenshot will have very minor small slice, if you hide the background, then you cant find that, it exists there. Here, is the screenshot:

enter image description here

Main idea is to declare how wide should labels be. To do this, you have to declare some rules.

Start with deciding, which label will have bigger priority, so let's say you need bigger priority for left label, so set its Horizontal Content Hugging Priority bigger than of right label

enter image description here

Now for both labels set their minimal width. This you can achive by setting width constraint with certain constant and relation Greater Than or Equal

enter image description here


enter image description here enter image description here

if you need to have always same "spacing" between labels, set their common constraint with relation Equal

enter image description here

One more approach with UIStackView,

Image 1Image 2Image 3

Notes:

  • UITableView should have,

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableView.automaticDimension
    
  • Keep Label 1 and Label 2 numberOfLines = 0

  • UIStackView should have Fill Proportionally distribution

Related