Question

I’m beginning to implement AutoLayout, and I’m trying to piece together how I can hook up one of my custom UITableViewCell classes properly. It usually has two UILabel objects, one on top of another, much like the standard subtitle class. I’d like these two labels to be centred in the cell, regardless of the cell’s height, with a given padding between the two.

I assume I can do this by doing something like add the heights of the two labels, add the padding, then subtract that from the height of the cell, and divide by two. However, I’m curious semantically whether this is correct, since I’d be constraining them from the top and bottom of the cell, rather than from each other. Am I missing a trick here?

Secondly, there’s sometimes a third label stacked in there too, so three on top of one another. In that instance, I’d need two sets of the padding, etc., but the question becomes even more relevant: shouldn’t I be constraining them to each other, rather than to the top and bottom of the cell?

So, question is more of a semantic one: if I want to constrain multiple elements vertically inside a parent view, is there a smarter way to do this than the method I suggested above?

(I’m currently implementing AutoLayout entirely in code (using Masonry) since this cell in question has no XIB and isn’t in a Storyboard).

Was it helpful?

Solution

The correct approach is to use a container view, which derives its height from its subviews. The container view is then pinned to the centre of the cell - you wouldn't have any constraints linking the container to the top and bottom edges of the cell.

Within the container, the vertical constraints would be |[label1]-[label2]|, which would make the container the height of the two labels plus the space, and the centre of the container view would be between the two labels.

If you added three labels, it would be |[label1]-[label2]-[label3]| and the centre of the container would be in the centre of the middle label.

In each case the centre of the container would be at the centre of the cell, and you don't need to calculate anything.

OTHER TIPS

Using Interface Builder, I'm able to vertically center multiple UIViews using the following steps. My use case is that I want these multiple UIViews to behave like a group as much as possible but I don't want to introduce any further views in order to achieve that.

Firstly, I select what I consider to be my default Form Factor using the button along the bottom (Form Factor being 3.5 inch or 4 inch retina). Then, I position the buttons in the center of the view by dragging one at a time, and I use the auto-snapping with blue guidelines to help with this.

Once the buttons are in place, I select them one at a time to apply their constraints. Click on a button, then select the Align Constraints menu from the floating buttons at the bottom right in Interface Builder (I see a set of 4, and the Align is the one on the left, next to the Form Factor button).

In this menu, check the Vertical Center In Container checkbox, then click to open the dropdown menu next to the value, which is probably 0. From that list, choose Use Current Canvas Value. Then, hit the Add Constraints button (possibly labelled Add 1 Constraint).

As you do this, you'll probably know it's working if you see the values next to Vertical Center In Container becoming set to Y-offset values you might guess are correct by glancing back to your arrangement of UIViews.

Apologies if you don't want to use Interface Builder, but I had trouble achieving this myself and wanted to mention these steps. You could always use IB as a one-off, and programmatically log out the constraints in a test, then take them and apply in your code-only solution.

Assuming you have label1, label2, label3, and cell

// Center the labels
[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                toItem:cell.contentView attribute:NSLayoutAttributeCenterX
                            multiplier:1.0 constant:0]
[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                toItem:cell.contentView attribute:NSLayoutAttributeCenterX
                            multiplier:1.0 constant:0]
[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                toItem:cell.contentView attribute:NSLayoutAttributeCenterX
                            multiplier:1.0 constant:0]

// Vertical alignment/spacing of 8 between each label
[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                toItem:cell.contentView attribute:NSLayoutAttributeTop
                            multiplier:1.0 constant:0]
[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                toItem:label1 attribute:NSLayoutAttributeBottom
                            multiplier:1.0 constant:8]
[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                toItem:label2 attribute:NSLayoutAttributeBottom
                            multiplier:1.0 constant:8]

You can nest everything inside a for() loop I guess so you won't have to repeat the constraints

enter image description here No code. Just edit in the storyboard.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top