I have an array of items to be display inside of my UITableViewCell
. Each of these items will be dynamically displayed using UILabel
. I use autolayout to setup the views layout.
Here's how I layout my tableViewCell
:
+------------------------------------------------+
| [cellView] |
| +---------------------------------------------+|
| |[otherView] <- fixed height ||
| | +------------------------------------------+||
| | |[UILabel] |||
| | +------------------------------------------+||
| +---------------------------------------------+|
| +---------------------------------------------+|
| |[itemView] ||
| | +---------------------------+ +------------+||
| | |[itemLabel] |-|[priceLabel]|||
| | +---------------------------+ +------------+||
| | +---------------------------+ +------------+||
| | |[itemLabel] |-|[priceLabel]|||
| | +---------------------------+ +------------+||
| | ||
| | --Add the next UILabels dynamically-- ||
| +---------------------------------------------+|
+------------------------------------------------+
For clarity
otherView
: have 2 UILabel
inside of it. Fixed height.itemView
: have unknown number of itemLabel
& priceLabel
inside. Will resize height based on the number of items
.itemLabel
& priceLabel
.Inside my cellForRowAtIndexPath
, I set the itemLabel
& priceLabel
constraints using constraintsWithVisualFormat:
for (NSDictionary *item in items) {
UILabel *itemLabel = [[UILabel alloc] init];
itemLabel.translatesAutoresizingMaskIntoConstraints = NO;
itemLabel.font = [UIFont systemFontOfSize:14.0f];
itemLabel.backgroundColor = [UIColor yellowColor];
itemLabel.text = [item valueForKey:@"item_name"];
[cell.itemView addSubview:itemLabel];
UILabel *priceLabel = [[UILabel alloc] init];
priceLabel.translatesAutoresizingMaskIntoConstraints = NO;
priceLabel.font = [UIFont systemFontOfSize:14.0f];
priceLabel.textAlignment = NSTextAlignmentRight;
priceLabel.backgroundColor = [UIColor greenColor];
priceLabel.text = [NSString stringWithFormat:@"RM %@", [item valueForKey:@"price"]];
[cell.itemView addSubview:priceLabel];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(cell.itemView, itemLabel, priceLabel);
[cell.itemView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[itemLabel]-5-[priceLabel(70)]|"
options:0
metrics:nil
views:viewsDictionary]];
[cell.itemView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[itemLabel]|"
options:0
metrics:nil
views:viewsDictionary]];
[cell.itemView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[priceLabel]|"
options:0
metrics:nil
views:viewsDictionary]];
}
The end result is as below. The only problem is that the itemLabel
and priceLabel
created are overlapping with each others instead of aligning nicely from top to bottom.
How do I set the constraints properly so that the itemLabel
& priceLabel
will align nicely from top to bottom and the itemView
will resize based on the number of items inside it?
You have insufficient constraints. The problem is that you are not setting constraints between, let's say, one itemLabel
and the previous itemLabel
. As you loop, you need to keep track of the previous item label so you can space this one down from it.
To put it another way: the technique you are looking for is that there are three cases:
The first label. It is pinned to whatever is above it (possibly the top of the superview).
All other item labels except the last. Each is pinned to the previous label.
The last label. It is pinned to the previous label and to the bottom, thus giving the cell its height.
By an incredible coincidence, I actually have on hand some code that illustrates this principle; the situation is obviously not 100% identical to yours, but the principle is exactly the sort of thing you want to be following here:
UILabel* previousLab = nil;
for (int i=0; i<30; i++) {
UILabel* lab = [UILabel new];
// lab.backgroundColor = [UIColor redColor];
lab.translatesAutoresizingMaskIntoConstraints = NO;
lab.text = [NSString stringWithFormat:@"This is label %d", i+1];
[sv addSubview:lab];
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[lab]"
options:0 metrics:nil
views:@{@"lab":lab}]];
if (!previousLab) { // first one, pin to top
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(10)-[lab]"
options:0 metrics:nil
views:@{@"lab":lab}]];
} else { // all others, pin to previous
[sv addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:@"V:[prev]-(10)-[lab]"
options:0 metrics:nil
views:@{@"lab":lab, @"prev":previousLab}]];
}
previousLab = lab;
}
// last one, pin to bottom, this dictates content size height
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"V:[lab]-(10)-|"
options:0 metrics:nil
views:@{@"lab":previousLab}]];