可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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>
回答1:
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.
回答2:
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.
回答3:
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
回答4:
I got this error in all sorts of circumstances (not necessarily tied to UICollectionView and friends as suggested by the correct answer here)..
So my way of dealing with it was simply clearing all the constraints then building them again (only this time i have no fear of my constraints colliding with these pre-created ones):
so in code:
UIView *parentView = [viewInQuestion superview];
[parentView clearConstraintsOfSubview:viewInQuestion];
where clearConstraintsOfSubview
is a category method on UIView:
- (void)clearConstraintsOfSubview:(UIView *)subview
{
for (NSLayoutConstraint *constraint in [self constraints]) {
if ([[constraint firstItem] isEqual:subview] || [[constraint secondItem] isEqual:subview]) {
[self removeConstraint:constraint];
}
}
}
回答5:
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.
回答6:
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.
回答7:
I was having a similar problem found from testing Split View on the iPad Pro, and DesignatedNerd's answer worked but I didn't need so much code. Here is what I used:
[self.tableView.tableHeaderView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.tableView
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0];
NSLayoutConstraint *yConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.tableView
attribute:NSLayoutAttributeTop
multiplier:1
constant:0];
[self.tableView addConstraints:@[widthConstraint, yConstraint]];
Note the addition of the Y Constraint, which binds the top of the tableHeaderView to the top of the tableView.
回答8:
I had the same issue when adding constraints to a Table view header. It seem to occur when adding constraints with set constants when the bounds of the header was (0,0,0,0). I managed to fix this by only adding the constraints in the layout subviews method when the bounds of the header was not (0,0,0,0)
if self.bounds == CGRect.zero {
return
}
回答9:
In my case, I was inadvertently setting up my programmatic constraints twice. As soon as I removed the duplicate call, the conflicts went away.
回答10:
I catch this problem when I use AL create tableviewHeader
I init tableview like below
let table = UITableView.init(frame: .zero, style: .grouped)
// set table constraint ...
then I create tableviewHeader with AutoLayout.
"<NSLayoutConstraint:0x600003d7d130 'UIView-Encapsulated-Layout-Width' UIView:0x7fe92bc55260.width == 0 (active)>"
symbolic breakpoint appear
After I refer @Yerk 's the answer.
I change the frame when I init tableView
let rect = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 0)
let table = UITableView.init(frame:rect , style: .grouped)
The problem seems to be solved