Skip to content

Requesting help understanding 'Unable to simultaneously satisfy constraints.' #16

Closed

Description

@danzaner

Hi smileyborg,

First, thanks a ton for creating PureLayout and this TableViewCellWithAutoLayout sample. It's a huge help.

Your sample app works great. No errors, everything looks good in the simulator. My strategy is to add as little as possible to your sample to understand how each piece works, but I'm hitting an 'Unable to simultaneously satisfy constraints' error almost immediately.

All I am trying to do is insert a UIView with a colored background (like in your example apps) in between the titleLabel and bodyLabel. I can do it, but not without XCode complaining and breaking a constraint.

I started by adding a new UIView to RJTableViewCell.h:

@property (strong, nonatomic) IBOutlet UIView *storyView;

Then I instantiated storyView in initWithStyle:reuserIdentifier by adding these lines just before where contentView's background color is set:

    self.storyView = [UIView newAutoLayoutView];
    self.storyView.backgroundColor = [UIColor colorWithRed:0 green:1 blue:1 alpha:0.1];

Then in updateConstraints, I uncommented:

self.contentView.bounds = CGRectMake(0.0f, 0.0f, 99999.0f, 99999.0f);

and added this after the calls on self.titleLabel:

    [self.storyView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.titleLabel withOffset:kLabelVerticalInsets relation:NSLayoutRelationEqual];

    [UIView autoSetPriority:UILayoutPriorityRequired forConstraints:^{
        [self.storyView autoSetContentCompressionResistancePriorityForAxis:ALAxisVertical];
    }];

    [self.storyView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:kLabelHorizontalInsets];
    [self.storyView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:kLabelHorizontalInsets];
    [self.storyView autoSetDimension:ALDimensionHeight toSize:25.0];

And finally, I changed:

[self.bodyLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.titleLabel withOffset:kLabelVerticalInsets relation:NSLayoutRelationGreaterThanOrEqual];

to:

[self.bodyLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.storyView withOffset:kLabelVerticalInsets relation:NSLayoutRelationGreaterThanOrEqual];

I get what I expected -- a blue-green rectangle 25 pixels high exactly as wide as titleLabel and bodyLabel with 10 pixels of space above and below. I also get this:

2015-01-28 19:29:43.847 TableViewCellWithAutoLayout[41969:3383075] 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:0x7f8da3036bb0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8da302b860(44)]>",
    "<NSLayoutConstraint:0x7f8da3037ec0 V:|-(10)-[UILabel:0x7f8da302c900'Marion']   (Names: '|':UITableViewCellContentView:0x7f8da302b860 )>",
    "<NSLayoutConstraint:0x7f8da3038390 V:[UILabel:0x7f8da302c900'Marion']-(10)-[UIView:0x7f8da302d350]>",
    "<NSLayoutConstraint:0x7f8da30369f0 V:[UIView:0x7f8da302d350(25)]>",
    "<NSLayoutConstraint:0x7f8da3036b20 V:[UIView:0x7f8da302d350]-(>=10)-[UILabel:0x7f8da302cf20'Lorem ipsum dolor sit ame...']>",
    "<NSLayoutConstraint:0x7f8da3036590 UILabel:0x7f8da302cf20'Lorem ipsum dolor sit ame...'.bottom == UITableViewCellContentView:0x7f8da302b860.bottom - 10>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f8da30369f0 V:[UIView:0x7f8da302d350(25)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

I've experimented with every variation I can think of. No matter what I do, I get this constraint problem in XCode. It doesn't seem like I've done much to your sample, but I assume I've creating ambiguity somewhere. If you can shed any light on it, I'd be grateful.

Thanks in advance,
Dan

Activity

  1. smileyborg commented on Jan 29, 2015

    @smileyborg
    Owner

    Hey Dan, first of all, major 👍 on the extremely clear write up! If only everyone asked questions with this degree of detail :)

    Your learning strategy is great -- slowly adding piece by piece, that's exactly what I'd recommend.

    Now, to the issue that you're seeing. Generally speaking, there are 2 types of constraint issues:

    • Ambiguity: too few constraints (more than one valid layout)
    • Overspecification: too many constraints (conflicting required constraints)

    Whenever you see a Unable to simultaneously satisfy constraints. message, it's definitely the latter. When you have this happen, the next step is to look through the list of conflicting constraints to understand what went wrong. (By the way, the constraint that the internal auto layout engine decides to break in order to "recover" is done totally at random, so whichever constraint it chooses to break has no special meaning.)

    You have to be somewhat familiar with the VFL syntax in order to be able to read and understand the printed constraints. Basically, because all of them have a V: that means the issue is along the vertical axis. Next, you'll notice that the bottom 5 constraints in the list all translate to constraints you specifically added and intend to keep. So it's pretty easy to hone in on the first one, which as you can see by the additional name it has (UIView-Encapsulated-Layout-Height) it has been added internally by UIKit.

    What does this constraint do? Based on its VFL description, it fixes the height of the table view cell's contentView at exactly 44 pt, which happens to be the default initial height of a cell when first created. The reason it wasn't a problem before but is a problem now with your changes is because you've added just enough additional content & spacing between views so that the absolute minimum height required is something more than 44 pt...and as you can see, you're getting a constraint exception because the cell/contentView starts out very small (at 44 pt) until it's actually laid out with the correct size for the content later on.

    So the short and simple is that what you're seeing isn't anything wrong with your constraints or additions. In previous iOS versions, setting the contentView's size to something really large would workaround this issue, which is what the recommendation to uncomment this line was:

    self.contentView.bounds = CGRectMake(0.0f, 0.0f, 99999.0f, 99999.0f);
    

    It looks like as of iOS 8.0 or 8.1, Apple has slightly changed how table view cells work internally, and the proper workaround for this whole issue may be to set the cell's size (instead of the contentView's) since there is clearly now an internal constraint fixing the size of the contentView (e.g. if we manually set the size of the contentView, it has no effect since the cell -- its superview -- is still 44 pt tall). So, give this a try:

    self.bounds = CGRectMake(0.0f, 0.0f, 99999.0f, 99999.0f);
    

    I haven't tested that out, but based on what you have described that might fix the issue. Let me know how it goes, there are some other things to try if that doesn't work.

  2. danzaner commented on Jan 29, 2015

    @danzaner
    Author

    Hey, smileyborg!

    Thank you for the amazing turnaround on my question and the fantastic detail! That was so much more helpful than just saying, "Try setting self.bounds". I know I have a ton to learn, but I feel like the examples I've got from you are going to make the difference.

    It turns out that your guess was correct -- you do need to set the bounds on the cell itself -- but, you have to set the bounds on the cell's contentView as well. I guess Apple is setting both objects' heights to 44 pt internally. In any case, the errors are now gone! Thanks again for your help and thanks again for open-sourcing a great project!

    Best,
    Dan

  3. smileyborg commented on Jan 29, 2015

    @smileyborg
    Owner

    That makes sense, if you don't want to set both of them, setting the cell's size and then calling:

    [self setNeedsLayout];
    [self layoutIfNeeded];
    

    (where self is the cell) should cause the contentView to automatically be adjusted based on the new cell size. (Why? Forcing a layout pass as above will cause the constraints internal to the cell to be solved, which should end up adjusting the contentView's size since it is likely set to exactly match the cell's size.)

  4. jkyin commented on Jul 9, 2015

    @jkyin

    Nice. Good job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions