Auto-layout: What creates constraints named UIView

2019-01-10 08:55发布

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>

10条回答
戒情不戒烟
2楼-- · 2019-01-10 09:24

We've started seeing tons of layout conflicts in iOS 11 that include references to these constraints and they are in fact added via the translatesAutoresizingMaskIntoConstraints flag. It seems that in iOS 11 there's a lot more AutoLayout magic happening when a view is added to the hierarchy rather than just when the view is laid out (as it seemed to work in previous iOS versions).

This is the case that we were running into:

  • Create a view whose internal layout helps define the views size (e.g., the view has internal constraints that includes explicit padding, etc.)
  • *** Add this view to the hierarchy.
  • Set the translatesAutoresizingMaskIntoConstraints to false some time later, before the layout pass.

The second step (***) will result in a conflict because the system will add zero size constraints to the view at the time the view is added to the hierarchy. We were setting translatesAutoresizingMaskIntoConstraints later as a result of using the PureLayout framework which automatically sets this flag correctly when you constrain the view... That said, in iOS 11 you need to remember to turn off translatesAutoresizingMaskIntoConstraints at construction time, before the view is added to the hierarchy.

I suspect Apple thought that defaulting this flag to YES would be way more helpful than it is painful. Unfortunately, this has not been the case.

查看更多
成全新的幸福
3楼-- · 2019-01-10 09:24

In my case, I was inadvertently setting up my programmatic constraints twice. As soon as I removed the duplicate call, the conflicts went away.

查看更多
戒情不戒烟
4楼-- · 2019-01-10 09:25

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.
查看更多
我想做一个坏孩纸
5楼-- · 2019-01-10 09:28

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.

查看更多
登录 后发表回答