Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto-layout: What creates constraints named UIView-Encapsulated-Layout-Width & Height?

My layout constraints are fine in Interface Builder but an exception occurs at runtime thanks to some part of the framework applying fixed height and width constraints that I really don't want. Why are they there, and how to turn them off?

They're the last two constraints shown in the logged list:

2014-04-26 09:02:58.687 BBCNews[32058:60b] 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. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>",
    "<NSLayoutConstraint:0xbf47190 UIView:0xbf4a3c0.leading == BNMyNewsCell_landscape:0xbf48b10.leading>",
    "<NSLayoutConstraint:0xbf47160 UIView:0xbf4a3c0.trailing == BNMyNewsCell_landscape:0xbf48b10.trailing>",
    "<NSLayoutConstraint:0xbf47130 BNMyNewsCell_landscape:0xbf48b10.bottom == UIView:0xbf4a3c0.bottom>",
    "<NSLayoutConstraint:0xbf47100 UIView:0xbf4a3c0.top == BNMyNewsCell_landscape:0xbf48b10.top>",
    "<NSLayoutConstraint:0xd4c3c40 'UIView-Encapsulated-Layout-Width' H:[BNMyNewsCell_landscape:0xbf48b10(304)]>",
    "<NSLayoutConstraint:0xd4c38a0 'UIView-Encapsulated-Layout-Height' V:[BNMyNewsCell_landscape:0xbf48b10(290)]>"
}
Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>
like image 550
Reuben Scratton Avatar asked Apr 26 '14 08:04

Reuben Scratton


People also ask

What is UIView encapsulated layout width?

If you see the error message contains UIView-Encapsulated-Layout-Width , which means your constraints relative to width conflict with it. For example, you set label constrains: [ leading , trailing , bottom , top ] or [ CenterX , leading , bottom , top ] leading trailing and CenterX are for width.

What is Nslayoutconstraint?

The relationship between two user interface objects that must be satisfied by the constraint-based layout system.

What is translatesAutoresizingMaskIntoConstraints?

translatesAutoresizingMaskIntoConstraints. A Boolean value that determines whether the view's autoresizing mask is translated into Auto Layout constraints.


4 Answers

This may not answer your question, but it could help others like me who got here from search.

I was getting a strange AutoLayout broken constraint error accompanied by a UIView-Encapsulated-Layout-Width constraint because I was adding a tableHeaderView to a table view that hadn't been sized with AutoLayout yet. So the system was trying to apply my header subviews' constraints inside a tableview with a frame of {0,0,0,0}. Since UITableView likes control over the width of its elements, its generated width constraint, UIView-Encapsulated-Layout-Width, was set to zero, causing all kinds of confusion with my header elements that were expecting 320+pt width.

The takeaway: make sure you are adding/manipulating your supplementary/header/footer views after the tableview has been sized by AutoLayout.

like image 41
Yerk Avatar answered Oct 14 '22 01:10

Yerk


Based on a ton of observation I believe (but cannot know for certain) that the constraints named UIView-Encapsulated-Layout-Width and UIView-Encapsulated-Layout-Height are created by UICollectionView and friends, and exist to enforce the size returned by the sizeForItemAtIndexPath delegate method. I guess it's there to ensure that the UICollectionViewCell set up by cellForItemAtIndexPath ends up the size that it was told it would be.

Which answers my initial question here. A second question is why were the constraints unsatisfiable? The cell's intrinsic height should have been the same as UIView-Encapsulated-Layout-Height. Again, I don't know for certain, but I suspect it was a rounding error (i.e. intrinsic height came to 200.1 pixels, the UIView-Encapsulated-Layout-Height maybe rounded to 200. The fix I came up with was to just lower the priority of the relevant cell constraint to allow UIView-Encapsulated-Layout-Height to have the last word.

like image 117
Reuben Scratton Avatar answered Oct 14 '22 01:10

Reuben Scratton


I was facing the same weird constraint and had no idea why, until I remembered the darned translatesAutoresizingMaskIntoConstraints property. Setting this to false solved the problem. What happens in the background is that the auto resizing masks (the old layout engine for iOS) are converted to constraints. Very often you don't want these constraints and want your own ones. In such cases you should set this property to false and you'll be fine:

view.translatesAutoresizingMaskIntoConstraints = false
like image 6
Zoltán Avatar answered Oct 14 '22 01:10

Zoltán


Definitely seeing this on a UITableView's tableHeaderView. I was able to get this to work with a custom header view by explicitly setting the width equal to that of the tableView after setting the tableHeaderView, THEN resetting it after a layout pass has completed.

Example code for iOS 9, which assumes you have a UITableView passed into your method as tableView and an item to configure it as item:

//Create the header view
self.contentDetailHeaderView = MyCustomHeaderView()

//Turn on autolayout
self.contentDetailHeaderView.translatesAutoresizingMaskIntoConstraints = false

//Add the header to the table view
tableView.tableHeaderView = self.contentDetailHeaderView

//Pin the width  
let widthConstraint = NSLayoutConstraint(item: self.contentDetailHeaderView,
    attribute: .Width,
    relatedBy: .Equal,
    toItem: tableView,
    attribute: .Width,
    multiplier: 1,
    constant: 0)

tableView.addConstraint(widthConstraint)

//Do whatever configuration you need to - this is just a convenience method I wrote on my header view.
self.contentDetailHeaderView.setupForItem(item)

//Lay out the configured view
self.contentDetailHeaderView.layoutIfNeeded()

//Reset the table header view, because ¯\_(ツ)_/¯
tableView.tableHeaderView = self.contentDetailHeaderView

Couple of notes, mostly for when I look this up again because I have the memory of a goldfish:

  • You do not have to call this from viewDidLayoutSubviews - I was able to use this technique as long as the tableView has the appropriate width during setup.
  • You do need to make sure your header view is set up to automatically resize itself. I did this by creating a .xib and then making sure all items were pinned so that as the view changed width, the height would then update.
  • If you're trying to do this for viewForHeaderInSection, you're probably better off grabbing something offscreen which you can lay out a la this technique. I haven't had much luck with the self-sizing bits.
like image 4
DesignatedNerd Avatar answered Oct 14 '22 01:10

DesignatedNerd