52

I keep getting 'Unable to simultaneously satisfy constraints' exceptions (Xcode 5, iOS 7, both device and simulator), where one of the constraints in the list is something like this:

"<NSLayoutConstraint:0x165b23d0 'UIView-Encapsulated-Layout-Width'
H:[StoryPlayerCell:0x165affc0(0)]>"

I did not set this constraint myself. It's also not an NSAutoresizingMaskLayoutConstraint. But where does it come from? And how can I get rid of it?

I have no idea. And I can't find anything about 'UIView-Encapsulated-Layout-Width' in the Apple's documentation. Even the Google search returns nothing at all.

Any suggestions?

PS: I'm using this cell in a UICollectionView with a custom subclass of UICollectionViewFlowLayout. Maybe the 'Encapsulated-Layout' part has something to do with this?

Andriy
  • 2,767
  • 2
  • 21
  • 29
Goodsquirrel
  • 1,476
  • 1
  • 15
  • 29
  • Can you upload a sample project where this reproduces? – Léo Natan Feb 14 '14 at 17:37
  • Try [CSStickyHeaderFlowLayout](https://github.com/jamztang/CSStickyHeaderFlowLayout)'s sample. If you scroll normally, nothing happens, but if you scroll really slowly, the error is thrown. It's kinda random and hard to reproduce. – andreamazz Feb 14 '14 at 18:40
  • 1
    @andreamazz i played with the sample, i think these codes is causing the Autolayout exception. `CGFloat height = MAX(0, -y + maxY);` line 79 in CSStickyHeaderFlowLayout.m. if you use a static value for the height like 200, the exception won't happen anymore. i can't understand what exactly is UIView-Encapsulated-Layout, but seems like dynamically changing the UICollectionViewLayoutAttributes frame may cause that problem. in the sample project, it's the height, in PO's problem, i guess it's the width. Maybe i can dig a little bit more into it, if you think that would be helpful. – Xu Yin Feb 14 '14 at 19:42
  • Using a static value also breaks the parallax effect. Although Using `CGFloat height = MAX(1, -y + maxY);`, with 1 instead of 0 fixes the problem, maintaining the right parallax effect. Thanks a lot for your help, it was spot on. – andreamazz Feb 15 '14 at 15:49
  • @andreamazz do you think i can use that as an answer, since no body has been answering? – Xu Yin Feb 20 '14 at 18:40

7 Answers7

17

A quick and nice fix, is to assign 999 priority value to the custom constraints with high priority. The encapsulated & auto-generated constraints should dominate with higher priority than your constraints.

https://stackoverflow.com/a/25795758/590010

Community
  • 1
  • 1
DZenBot
  • 4,806
  • 2
  • 25
  • 27
  • 21
    Actually this would only be a quick but not a nice fix, because the auto–generated constraint wants the cell width to be 0, which is not what I want. – Goodsquirrel Oct 10 '14 at 08:26
  • 1
    By the way, if custom constraints have lower priority now, why would not Auto Layout set label width to be 0, according to its higher-priority system constraint? – Alexey Mar 29 '15 at 04:34
  • @Goodsquirrel Did you make any progress with this? – ray Jun 01 '15 at 23:27
4

i played with the sample, i think these codes is causing the Autolayout exception.

CGFloat height = MAX(0, -y + maxY); line 79 in CSStickyHeaderFlowLayout.m.

if you use a static value for the height like 200, the exception won't happen anymore. i can't understand what exactly is UIView-Encapsulated-Layout, but seems like dynamically changing the UICollectionViewLayoutAttributes frame may cause that problem. in the sample project, it's the height, in PO's problem, i guess it's the width. Maybe i can dig a little bit more into it, if you think that would be helpful

Xu Yin
  • 3,932
  • 1
  • 26
  • 46
2

My situation

For me, I knew (logically) that my programmatic constraints should have worked. I was updating them after rotation of the device.

My quick-and-easy solution without changing constraints was to make sure that this was all done on the main thread. Why? I'm not too sure, but I assume that the rotation update must be in an asynchronous thread.

How I fixed it

From:

topTitleLeadingAnchorLandscape.isActive = true

To:

DispatchQueue.main.async {
    self.topTitleLeadingAnchorLandscape.isActive = true
}

Hope this saves someone lots of time in the future!

George
  • 25,988
  • 10
  • 79
  • 133
2

I often get this issue as a result of requesting that a collection view (or one of its parents) be laid out before it is actually part of the app's view hierarchy. What basically happens is this: lacking a UIWindow to determine the size of the new view controller's view, the system assigns an arbitrary width of 50 points to the collection's cells. This is often too small to contain its subviews, leading to the clash of constraints.

For example, if I call layoutIfNeeded during viewDidLoad then the view controller's view is still floating around without a parent and does not know how big it is. Besides, it would be unnecessary to perform layout at this point since the layout will be invalidated and recalculated when it is added to the hierarchy anyway.

Now, one could get around this as some have suggested by making some constraints optional but with very high priorities. This can be a useful general strategy, but in this case it likely is not. For example, if the content of the cell is inset from the sides of the cell by a certain amount, it's only likely to work if the constraint between the content and the cell's contentView is not at the required priority. This will make the size of the cell ambiguous, and will throw a different warning.

Messing with constraint priorities without knowing exactly what you're doing can also lead to unforeseen issues, especially when two constraints in the same axis have the same priority and parent. A common issue with stack views is when its children all have the same priority in its primary axis so it can't tell which one to shrink or expand when it cannot be its preferred size.

The best solution in my opinion is to try and avoid laying out the view before it is added to its parent. Add a Constraint breakpoint and run the app. Note the method in which the app stops for the warning; it will probably be on a call to layoutIfNeeded. Now, replace that with the following:

if view.superview != nil {
    view.layoutIfNeeded()
}

This may be all that you need to do. If you still experience this problem then it may be that the collection view's cells are too small, either due to a simple size miscalculation, a problem in its layout object's code, or that the cell's constraints force it to be a specific size that is not the same as the one it is being given by the layout.

Ash
  • 9,064
  • 3
  • 48
  • 59
1

I found this with iOS 10 builds where I was using UITableViewCell subclasses as normal views not as table rows. The cell was constraining its contentView to zero width.

The workaround I used was to just add the contentView to the view hierarchy instead of the tableview cell. I also made sure to retain the tableView cell (as it was no longer being retained by the view hierarchy itself).

Joseph Lord
  • 6,446
  • 1
  • 28
  • 32
0

I can shed some light on the answer to the question. In my OS X app, I found such a mysterious constraint on the cell view of an NSTableView, (that is, an object which I had previously returned in the NSTableViewDelegate method -tableView:viewForTableColumn:row). I had also sent a setWidth:0.0 message to this same table column. Changing the parameter in that setWidth: from 0.0 to something else was reflected in the constant value of the mysterious constraint.

Conclusion: Constraints which log themselves with 'UIView-Encapsulated-Layout-Width' or 'NSView-Encapsulated-Layout-Width' are caused by setting the width of a table column, or something similar.

Jerry Krinock
  • 4,860
  • 33
  • 39
0

I was having this issue with an NSView based table cell that was being added to NSTableView. I set a fixed width on the NSView because it must be at least that size to properly accommodate the content. However it could be larger.

Changing the width constraint from NSLayoutRelationEqual to NSLayoutRelationGreaterThanOrEqual eliminated the warning.

Going from this:

[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:panelWidth]];

to this, fixed it:

[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:panelWidth]];

This isn't a 100% solution because when the NSTableView decides it needs to be smaller for reasons I am still not clear about, it will still throw the breaking constraint warning.

Cliff Ribaudo
  • 8,932
  • 2
  • 55
  • 78