RSS

Troubleshooting: iOS auto layout warning about UIView-Encapsulated-Layout-Height

Troubleshooting: iOS auto layout warning about UIView-Encapsulated-Layout-Height. The UI layout correctly, sounds the warning is false but actually there is an error in code.

Symptom

In a table view cell layout, there is no layout constraints issue in xcode. However during run time, xcode console report following layout constraints issue:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want.
    Try this:
        (1) look at each constraint and try to figure out which you don't expect;
        (2) find the code that added the unwanted constraint or constraints and fix it.
(
    "<NSLayoutConstraint:0x6000012c0190 UIView:0x7fdefe74a240.height == 1   (active)>",
    "<NSLayoutConstraint:0x6000012c0960 MyModule.MyAwesomeControl:0x7fdefe721d90.height == 48   (active)>",
    "<NSLayoutConstraint:0x6000012c40a0 V:|-(16)-[UIImageView:0x7fdefe747ae0]   (active, names: '|':UITableViewCellContentView:0x7fdefe747940 )>",
    "<NSLayoutConstraint:0x6000012c4410 V:[UIImageView:0x7fdefe747ae0]-(12)-[UIView:0x7fdefe74a240]   (active)>",
    "<NSLayoutConstraint:0x6000012c4690 V:[UIView:0x7fdefe74a240]-(20)-[UITextView:0x7fdefe8f3400'No network is currently d...']   (active)>",
    "<NSLayoutConstraint:0x6000012c47d0 V:[MyModule.MyAwesomeControl:0x7fdefe721d90]-(50)-|   (active, names: '|':UITableViewCellContentView:0x7fdefe747940 )>",
    "<NSLayoutConstraint:0x6000012c4820 MyModule.MyAwesomeControl:0x7fdefe721d90.top == UITextView:0x7fdefe8f3400'Awesome text here...'.top + 56   (active)>",
    "<NSLayoutConstraint:0x6000012b9040 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fdefe747940.height == 32   (active)>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000012c0960 MyModule.MyAwesomeControl:0x7fdefe721d90.height == 48   (active)>

The layout constraints include UIView-Encapsulated-Layout-Height which not part of original view layout constraints.

However from the UI perspective, thought this warning exist, the layout looks correctly, it didn’t break the constraint mentioned above. It actually break 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fdefe747940.height == 32. It is kind of weird.

Analyze

What is UIView-Encapsulated-Layout-Height

The UIView-Encapsulated-Layout-Height constraint is added by UITableView, the value 32 (or whatever number) should be cell height. However in this case the actual cell height is not 32. 32 is estimated row height.

Auto Height for TableView Cell

In iOS, you can use Auto Layout to define the height of a table view cell; however, the feature is not enabled by default.

Normally, a cell’s height is determined by the table view delegate’s tableView:heightForRowAtIndexPath: method. To enable self-sizing table view cells, you must set the table view’s rowHeight property to UITableViewAutomaticDimension. You must also assign a value to the estimatedRowHeight property. As soon as both of these properties are set, the system uses Auto Layout to calculate the row’s actual height.

First make sure let UITableView determine its cell height automatically with use following code:

tableView.estimatedRowHeight = 32; // or any number, but need set a number to make it work.
tableView.rowHeight = UITableView.automaticDimension

After this settings, the cell should calculate its height automatically.

Then lay out the table view cell’s content within the cell’s content view. To define the cell’s height, you need an unbroken chain of constraints and views (with defined heights) to fill the area between the content view’s top edge and its bottom edge. If your views have intrinsic content heights, the system uses those values. If not, you must add the appropriate height constraints, either to the views or to the content view itself.

Additionally, try to make the estimated row height as accurate as possible. The system calculates items such as the scroll bar heights based on these estimates. The more accurate the estimates, the more seamless the user experience becomes.

The Real Scenario

From the UI perspective, thought this warning exist, the layout looks correctly, it didn’t break the constraint mentioned above. It actually break 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fdefe747940.height == 32. Looks like Xcode give wrong warning at the first place.

Root Cause

After code review I found a call to layoutIfNeeded() after change some view attributes. I suspect maybe this caused issue, the reason is when call layoutIfNeeded() it lays out the subviews immediately. Since the cell height is not determined yet, so it use estimated height and caused warning.

After actual cell height is determined the cell will layout one more time with correct cell height. This explain why UI looks correct and not use estimated row height.

layoutIfNeeded()

Declaration

func layoutIfNeeded()

Discussion

Use this method to force the view to update its layout immediately. When using Auto Layout, the layout engine updates the position of views as needed to satisfy changes in constraints. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root. If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.

Solution

First make sure table view cell is set to auto height:

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 32;

Then the solution to remove this weird UIView-Encapsulated-Layout-Height warning is simple: find and remove un-necessary layoutIfNeeded(). Then Xcode will not report any layout constraints warnings and layout is correct.

References

DNS Firewall for iOS

Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps

Ad