0

I'm trying to use dynamic heights in a UITableView with a specific cell layout. Consider the following illustrative representation of that layout:

enter image description here

I have the horizontal constraints working properly (15px from both edges, 15px between them, equal widths) but I'm struggling with the vertical constraints. Here are the vertical requirements for this layout:

  1. The vertical intrinsic content size of both the green and blue rectangles are based on external data which is passed to the cell at the time of creation.
  2. Both rectangles are vertically centered within their superview
  3. There will always be a minimum space of 15px between the top/bottom edges of the rectangles and the respective edges on the superview. In other words, whichever one is taller dictates the height of the superview (i.e. the cell)

To that end, here's what I have constraint-wise so far:

  1. Vertical center constraints for both rectangles
  2. Height constraints of the rectangles equal to or less than the height of the superview minus 30 (i.e. if the rectangle's height is 40, the superview must be a minimum of 70. This theoretically achieves the same effect as setting separate top and bottom '>= 15' constraints while using two less.)
  3. Vertical content Hugging on the superview set to 'required' (i.e. 1000)

The third point is because the second points together only define the minimum height for the superview (yellow), but not a maximum. In theory, if it had a height of 10,000 it would still satisfy those constraints.

My thought is setting its content hugging to 'required' would make the superview as short as possible without violating the other constraints, thus at all times, either the green rectangle or the blue rectangle would be 15 px from the edge depending on whichever was taller. However, the height still seems to be 'stretched out' as seen here...

enter image description here

Note: The views on the inside are properly vertically centered and correctly maintain a minimum distance from the top/bottom edges. The problem I'm trying to solve is restricting the height of the superview to be as small as possible.

It doesn't appear that I'm getting any ambiguous constraint messages (I don't see anything in the logs, but I believe I should be because again <= constraints aren't enough on their own, so I'm not sure exactly how to use the tools to debug this, or to find out which constraint is driving the height.

So, can anyone help?

P.S. To ensure it wasn't something external to the cell, like forgetting to configure auto-heights for the UITableView, I removed the two rectangles and replaced them with a simple multi-line label pinned to all four edges. When I ran it with that, the cell properly shrank in size as expected. I bring that up to hopefully stave off answers suggesting that's potentially the problem.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • did you try to set bottom and top constraint with padding 15 or greater? means the relation >= – Björn Ro Oct 14 '16 at 05:04
  • Yes, tried that too. Technically that should achieve the same result as using the heights with the constant of 30. – Mark A. Donohoe Oct 14 '16 at 05:06
  • than you need to set the height constraint when the layot gets changed. means create am iboutlet of the height constraint and change the value, and than the bottom and top with greater equal 15. – Björn Ro Oct 14 '16 at 05:08
  • But that still doesn't dictate the maximum height, only the minimum. That's the issue. – Mark A. Donohoe Oct 14 '16 at 05:10
  • as you know, the cell rezises on the content you have inside of your cell, like a scrollView would do. So the edges must match with the constraints – Björn Ro Oct 14 '16 at 05:10
  • Obviously. That's why I set the heights and vertical centers. The problem is it defines a minimum height, not a maximum, which is why I set content hugging. If you still think I'm missing something, mind posting a working example? – Mark A. Donohoe Oct 14 '16 at 05:12
  • when you set to both rects the bottom and top constraints to 15 or greater it should have the effect, that the smaller on will have more space to the superview and the bigger one will have 15. Ehm, did you set the estimatedRowHeight? And did you set rowHeight to AutomaticDimensions?. – Björn Ro Oct 14 '16 at 05:12
  • oh and i think it has nothing todo with priority or hugging, you would need this when the objects are fighting about more space for them. Sorry, currenrly i cannot create an example – Björn Ro Oct 14 '16 at 05:15
  • @MarqueIV have you tried using the xCode visual debugger? That may help you in identifying which constraints are causing the problem – Shayan Jalil Oct 14 '16 at 05:16
  • Yeah, I looked at that but I can't tell which is driving the height. Do you think the content hugging should counter the ambiguity of the <= constraints? If not, what should one use? – Mark A. Donohoe Oct 14 '16 at 05:18
  • This is just a suggestion, but I think a height constraint on the superview is not required, since you are calculating it's height based on the two internal views. And these two views have top bottom constraints with the superview, so the superview's height would automatically get calculated using these constraints – Shayan Jalil Oct 14 '16 at 05:18
  • Well, heights, not edges, but yes. But again, they dictate a minimum height. What dictates the maximum? That's what I can't figure out. I thought content hugging would do that but obviously not. – Mark A. Donohoe Oct 14 '16 at 05:20
  • in my opinion you can set the hugging priority back to default. Make sure the rowHeight is set to AutomaticDimensions, the height of each rect will change over the heightConstraint , the rects are placed in center for the vertical positioning and the bottom and top constraints are 15px or greater. – Björn Ro Oct 14 '16 at 05:20
  • Yes, it's already set to automatic as I specifically called out in the note, I tried a different view and it worked fine. And I had the constraints set up the way you did, but that did not work. That's why I tried using heights instead of edges, but that didn't work either. Again, nothing is dictating the maximum height. That's the problem I'm trying to solve. – Mark A. Donohoe Oct 14 '16 at 05:22
  • You can debug your constraints as described here: http://stackoverflow.com/questions/26389273/how-to-trap-on-uiviewalertforunsatisfiableconstraints – koen Oct 14 '16 at 15:17

2 Answers2

0

Reading the requirements you provided I have added constraints shown below:

enter image description here

For demonstration purpose I have used a simple view container instead of a cell. Since inner boxes will have intrinsic content size derived externally, I have taken labels to mimic that.

To reiterate, constraints added are:

Horizontal

  1. Container view(orange) has leading and trailing constraints with the super view.
  2. Inner views has leading, trailing constraints with 15points of space.
  3. Both labels have leading and trailing constraints with 9 points.
  4. Both inner views have equal width constriant.

Vertical

  1. Container view is vertically in center.
  2. Both inner views have vertically center constraint applied.
  3. Both inner views have top and bottom constraints with >= 15 condition.
  4. Both inner labels have top and bottom constraints with their super views.

I set the no. of lines property of both labels to zero so that they can grow at run time.

At runtime I changed the text of one of the label and the box and container grew accordingly.

enter image description here

enter image description here

To refresh your cell height implement heightForRow method and provide the latest height. A typical snippet will look something like this (assuming you have already initialized the cell):

cell.needsUpdateConstraints()
cell.updateConstraintsIfNeeded()
cell.contentView.setNeedsLayout()
cell.contentView.layoutIfNeeded()
let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1
return height

Hope this will help.

HAK
  • 2,023
  • 19
  • 26
  • Hi @HAK! I wasn't clear enough in my question. Sorry about that. It's been updated. Unfortunately the issue I'm having is within a UITableView with auto-sizing. The inside views already work properly. It's the outside height (your orange rect) that doesn't, which you've pinned to the superview here, avoiding the issue I'm having. Can you put this in a UITableViewCell and try it again? That's the issue since there is no restriction on height there. – Mark A. Donohoe Oct 14 '16 at 12:55
  • To update the cell height, you need to reload the table or the particular section your cell resides in. Also you need to implement heightForRow method of table view every time providing the latest height. – HAK Oct 14 '16 at 13:13
  • You don't provide the height (or rather you let it provide the default of -1) when using auto-sizing cells. And reloading the data isn't needed either unless the heights change after the cell has been added to the table. In this case, the data is being set in the cellForRowAtIndexPath which means its already ready when the cell is added. – Mark A. Donohoe Oct 14 '16 at 13:31
0

Ok, so I was going in the right direction with the content hugging, but the correct way to handle this was to specify a height constraint on the yellow view of 0 and with a low priority. I used 100 to be even lower than the default 250. When I did that, the solver tries to get as close to zero as it can while still respecting the other constraints, therefore 'hugging' the content. Still don't know why content hugging on its own didn't work, but that addressed the issue.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286