What is NSLayoutConstraint "UIView-Encapsulated-Layout-Height" and how should I go about forcing it to recalculate cleanly?
Solution 1:
Try to lower the priority of your _collapsedtextHeightConstraint
to 999. That way the system supplied UIView-Encapsulated-Layout-Height
constraint always takes precedence.
It is based on what you return in -tableView:heightForRowAtIndexPath:
. Make sure to return the right value and your own constraint and the generated one should be the same. The lower priority for your own constraint is only needed temporarily to prevent conflicts while collapse/expand animations are in flight.
Addition: While it may be debatable if the system supplied constraint is correct or not, there's no point in fighting the framework. Simply accept that the system constraint takes precedence. If you think the system constraint is wrong, make sure to return the correct rowHeight from the delegate.
Solution 2:
I have a similar scenario: a table view with one row cell, in which there are a few lines of UILabel objects. I'm using iOS 8 and autolayout.
When I rotated I got the wrong system calculated row height (43.5 is much less than the actual height). It looks like:
"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"
It's not just a warning. The layout of my table view cell is terrible - all text overlapped on one text line.
It surprises me that the following line "fixes" my problem magically(autolayout complains nothing and I get what I expect on screen):
myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works
with or without this line:
myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem
Solution 3:
99.9% of the time, while using custom cells or headers, all conflicts of UITableViews
occur when the table loads of the first time. Once loaded you will usually not see the conflict again.
This happens because most developers typically use a fixed height or anchor constraint of some sort to layout an element in the cell/header. The conflict occurs because when the UITableView
first loads/being laid out, it sets the height of its cells to 0. This obviously conflicts with your own constraints. To solve this, simply set any fixed height constraints to a lower priority (.defaultHigh
). Read carefully the console message and see which constraint the layout system decided to break. Usually this is the one that needs its priority changed. You can change the priority like this:
let companyNameTopConstraint = companyNameLabel.topAnchor.constraint(equalTo: companyImageView.bottomAnchor, constant: 15)
companyNameTopConstraint.priority = .defaultHigh
NSLayoutConstraint.activate([
companyNameTopConstraint,
the rest of your constraints here
])
Solution 4:
I was able to get the warning to go away by specifying a priority on one of the values in the constraint the warning messages says it had to break (below "Will attempt to recover by breaking constraint"
). It appears that as long as I set the priority to something greater than 49
, the warning goes away.
For me this meant changing my constraint the warning said it attempted to break:
@"V:|[contentLabel]-[quoteeLabel]|"
to:
@"V:|-0@500-[contentLabel]-[quoteeLabel]|"
In fact, I can add a priority to any of the elements of that constraint and it will work. It doesn't seem to matter which one. My cells end up the proper height and the warning is not displayed. Roger, for your example, try adding @500
right after the 388
height value constraint (e.g. 388@500
).
I'm not entirely sure why this works but I've done a little investigating. In the NSLayoutPriority enum, it appears that the NSLayoutPriorityFittingSizeCompression
priority level is 50
. The documentation for that priority level says:
When you send a fittingSize message to a view, the smallest size that is large enough for the view's contents is computed. This is the priority level with which the view wants to be as small as possible in that computation. It's quite low. It is generally not appropriate to make a constraint at exactly this priority. You want to be higher or lower.
The documentation for the referenced fittingSize
message reads:
The minimum size of the view that satisfies the constraints it holds. (read-only)
AppKit sets this property to the best size available for the view, considering all of the constraints it and its subviews hold and satisfying a preference to make the view as small as possible. The size values in this property are never negative.
I haven't dug beyond that but it does seem to make sense that this has something to do with where the problem lies.