By Rog


2014-07-31 12:55:53 8 Comments

I have a UITableView running under iOS 8 and I'm using automatic cell heights from constraints in a storyboard.

One of my cells contains a single UITextView and I need it to contract and expand based on user input - tap to shrink/expand the text.

I'm doing this by adding a runtime constraint to the text view and changing the constant on the constraint in response to user events:

-(void)collapse:(BOOL)collapse; {

    _collapsed = collapse;

    if(collapse)
            [_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
        else
            [_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];

    [self setNeedsUpdateConstraints];

}

Whenver I do this, I wrap it in tableView updates and call [tableView setNeedsUpdateConstraints]:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView setNeedsUpdateConstraints];
// I have also tried 
// [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// with exactly the same results.

[tableView endUpdates];

When I do this, my cell does expand (and animates whilst doing it) but I get a constraints warning:

2014-07-31 13:29:51.792 OneFlatEarth[5505:730175] 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:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>",

    "<NSLayoutConstraint:0x7f94dced2260 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']-(15)-|   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced2350 V:|-(6)-[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced6480 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f94de5773a0(91)]>"



 )

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>

388 is my calculated height, the other constraints on the UITextView are mine from Xcode/IB.

The final one is bothering me - I'm guessing that UIView-Encapsulated-Layout-Height is the calculated height of the cell when it is first rendered - (I set my UITextView height to be >= 70.0) however it doesn't seem right that this derived constraint then overrules an updated user cnstraint.

Worse, although the layout code says it's trying to break my height constraint, it doesn't - it goes on to recalculate the cell height and everything draws as I would like.

So, what is NSLayoutConstraint UIView-Encapsulated-Layout-Height (I'm guessing it is the calculated height for automatic cell sizing) and how should I go about forcing it to recalculate cleanly?

13 comments

@Che 2018-11-26 20:20:46

I had this error when using UITableViewAutomaticDimension and changing a height constraint on a view inside the cell.

I finally figured out that it was due to the constraint constant value not being rounded up to the nearest integer.

let neededHeight = width / ratio // This is a CGFloat like 133.2353
constraintPictureHeight.constant = neededHeight // Causes constraint error
constraintPictureHeight.constant = ceil(neededHeight) // All good!

@migs647 2018-12-07 18:04:48

This was the comment that clued me into my issue. I have an image cell that loads dynamically (grows and shrinks) and a tableview with estimatedRowHeight = 50 and rowHeight = UITableViewAutomaticDimension. I was still breaking on constraints even though the tableview was the correct height. Turns out the separators were 0.3333 in height and that was what was kicking my image size constraint in the cell to be broken. After turning off separators all was well. Thanks Che for giving me what to look for.

@Anton 2018-12-15 20:29:20

In this case create an extra constraint, e.g., bottomMargin >= [email protected] AutoLayout tries to accommodate the extra 1 point, resizes the cell, gets confused because of the separator height, tries to break/relax some constraints, finds the one @900, and discards that. You get the layout you want without warnings.

@Anton Tropashko 2019-03-13 14:37:43

save my day. few hours anyway

@Clifton Labrum 2018-07-10 00:51:12

I was able to resolve this error by removing a spurious cell.layoutIfNeeded() that I had in my tableView's cellForRowAt method.

@John 2018-12-06 18:23:06

Yes, this solved the issue for me too. I was doing code layout contraints so I initially thought that I might miss something. Thanks

@Andrey Chernukha 2019-02-05 18:19:14

The same thing! Thanks!

@kathy zhou 2018-01-02 07:25:30

Set this view.translatesAutoresizingMaskIntoConstraints = NO; should resolve this issue.

@Enkha 2019-07-12 00:38:02

This worked perfectly for me, even if it was not the perfect solution.

@Phu Nguyen 2017-07-19 11:20:16

As mentioned by Jesse in question's comment, this works for me:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

FYI, this issue not occurs in iOS 10.

@airowe 2019-01-30 15:04:12

In Swift 4.2: self.contentView.autoresizingMask = [.flexibleHeight]

@Golden Thumb 2014-11-26 11:42:01

I have a similar scenario: a table view with one row cell, in which there are a few lines of UILabel objects. I'm using iOS 8 and autolayout.

When I rotated I got the wrong system calculated row height (43.5 is much less than the actual height). It looks like:

"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"

It's not just a warning. The layout of my table view cell is terrible - all text overlapped on one text line.

It surprises me that the following line "fixes" my problem magically(autolayout complains nothing and I get what I expect on screen):

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works

with or without this line:

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem

@Abdalrahman Shatou 2015-01-26 13:57:56

Finally! This is a correct answer. In WWDC session, it was mentioned, if you gonna use the automatic row height sizing, then you shall set an estimatedRowHeight or bad things happens. (Yes, Apple really nasty things happened)

@Rachid Finge Jr 2015-04-13 23:31:05

Indeed, simply setting the estimatedRowHeight property solved all my problems and thus seems mandatory. It's worth noting that rather than just setting the property to 2.0, it's better to actually estimate the row height for scrolling performance reasons.

@JRafaelM 2015-04-17 11:33:19

with Obj-C the only way that fixed the problem was overriding the method from the tableview delegate, thank you a lot! -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 2.0; }

@Benjohn 2015-05-12 18:15:04

FWIW, adding the estimate has made no difference at all for me.

@Benjohn 2015-07-20 12:26:10

Heh. I'm back at this same answer again, and went to implement it with joy and hope. Once again, it's made no difference for me :-)

@Paul T. 2015-09-01 04:28:19

yes, that's the only correct answer for UITableViewAutomaticDimension

@Matt 2015-11-05 21:55:55

Estimates returned by tableView:estimatedHeightForRowAtIndexPath: MUST be at least as large as the cell. Otherwise, the calculated height of the table will be less than the actual height, and the table may scroll up (e.g. after unwind segue returns to the table). UITableViewAutomaticDimension should not be used.

@Stan 2016-06-09 18:19:16

Not sure what performance impact this has (none visually), but I found that I still got issues if the intrinsic size of the subviews + constraints was larger than the estimated height. Setting the self.tableView.estimatedRowHeight = 1000 (some arbitrary large number) eliminated the incompatible constraint.

@Jinghan Wang 2016-09-05 19:20:01

If the gap between estimatedRowHeight and the real height is too large, the tableView may flicker during fast scrolling.

@Caio 2016-12-16 16:41:08

Can' add estimatedRowHeight,this make my scroll buggy and makes tableView jump and flicker after any reloadCell or reloadSections.

@Natalia 2016-12-21 04:41:35

if estimatedRowHeight doesn't work, then try tableView.translatesAutoresizingMaskIntoConstraints = false. I was having this constraint issue with a tableview in a container view of a primary view controller that already had the estimatedRowHeight set, and it was not until I made sure it wasn't making constraints from the autoresizing mask that I stopped getting a bunch of constraint errors.

@Mojo66 2016-12-30 11:43:17

Note that the lower the estimatedRowHeight the more often cellForRowAtIndexPath will be called initially. tableview height divided by estimatedRowHeight times, to be exact. On an 12 inch iPad Pro, this can potentially be a number in the thousands and will hammer the datasource and can result in a significant delay.

@J. Doe 2018-10-01 18:46:55

This works with Xcode 10 and swift 4.2 :)

@Alcides Eduardo Zelaya 2016-06-28 21:53:56

After spending some hours scratching my head with this bug I finally found a solution that worked for me. my main problem was that I had multiple nibs registered for different cell types but one cell type specifically was allowed to have different sizes(not all instances of that cell are going to be the same size). so the issue arose when the tableview was trying to dequeue a cell of that type and it happened to have a different height. I solved it by setting

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

whenever the cell had its data to calculate its size. I figure it can be in

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

something like

cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

Hope this helps!

@user1687195 2016-03-11 17:22:34

I was getting a message like this:

Unable to simultaneously satisfy constraints...
...
...
...
NSLayoutConstraint:0x7fe74bdf7e50 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fe75330c5c0(21.5)]
...
...
Will attempt to recover by breaking constraint NSLayoutConstraint:0x7fe0f9b200c0 UITableViewCellContentView:0x7fe0f9b1e090.bottomMargin == UILabel:0x7fe0f9b1e970.bottom

I'm using a custom UITableViewCell with UITableViewAutomaticDimension for the height. And I've also implemented the estimatedHeightForRowAtIndex: method.

The constraint that was giving me problems looked something like this

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];

Changing the constraint to this will fix the problem, but like another answer I felt that this wasn't correct, as it lowers the priority of a constraint that I want to be required:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[email protected][title][email protected]|" options:0 metrics:nil views:views];

However, what I noticed is that if I actually just remove the priority, this also works and I don't get the breaking constraint logs:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[title]-6-|" options:0 metrics:nil views:views];

This is a bit of a mystery as to what the difference is between |-6-[title]-6-| and |-[title-|. But specifying the size isn't an issue for me and it gets rid of the logs, and I don't need to lower the priority of my required constraints.

@user2962814 2015-11-06 03:55:59

TableView get height for cell at indexPath from delegate. then get cell from cellForRowAtIndexPath:

top ([email protected])
    cell
bottom ([email protected])

if cell.contentView.height:0 //<-> (UIView-Encapsulated-Layout-Height:[email protected]) top([email protected]) conflicted with (UIView-Encapsulated-Layout-Height:[email protected]),

because of they priorities are is equal 1000. We need set top priority under UIView-Encapsulated-Layout-Height's priority.

@Ortwin Gentz 2014-09-11 19:43:01

Try to lower the priority of your _collapsedtextHeightConstraint to 999. That way the system supplied UIView-Encapsulated-Layout-Height constraint always takes precedence.

It is based on what you return in -tableView:heightForRowAtIndexPath:. Make sure to return the right value and your own constraint and the generated one should be the same. The lower priority for your own constraint is only needed temporarily to prevent conflicts while collapse/expand animations are in flight.

@Rog 2014-09-16 13:21:10

That would achieve exactly the opposite of what I want. The UIView-Encapsulated-Layout-Height is wrong - it belongs to the previous layout.

@Ortwin Gentz 2014-09-16 14:03:45

The UIView-Encapsulated-Layout-Height constraint is added by UITableView once the heights are determined. I calculate the height based on the systemLayoutSizeFittingSize of the contentView. Here, the UIView-Encapsulated-Layout-Height doesn't matter. Then, the tableView sets the contentSize explicitly to the value returned by heightForRowAtIndexPath:. In this case it's correct to lower our custom constraints' priority because the tableView constraint must take precedence after the rowHeights are calculated.

@DZenBot 2014-09-17 23:57:05

After trying different ways of fixing this, assigning 999 to the conflicting custom constraints did they trick. Thank you @OrtwinGentz!

@Rog 2014-09-22 10:48:52

@ortwin heightForRowAtIndexPath is olde worlde isn't it? You should not implement it when you have autolayout and self sizing cells.

@Ortwin Gentz 2014-09-22 17:24:01

@RogerNolan we had to support iOS 7 as well.

@testing 2014-11-12 08:48:27

@OrtwinGentz: Still don't get the point it's correct to lower our custom constraints' priority because the tableView constraint must take precedence after the rowHeights are calculated. The thing is that UIView-Encapsulated-Layout-Height is wrong if I don't lower the priority...

@ndbroadbent 2015-02-10 12:51:29

This doesn't work for me... it just lets iOS break the constraint, so that my cells are shorter than I need them to be. How can I get iOS to recalculate the cell height instead?

@Ortwin Gentz 2015-02-10 15:04:15

@nathan.f77 When you change the cell size you have to tell the tableView: [tableView beginUpdates]; [tableView endUpdates]; Then the tableView will ask the delegate via -heightForRowAtIndexPath: for the new heights.

@ndbroadbent 2015-02-17 13:38:50

I'm not really changing the cell heights, I'm trying to reuse an existing cell from dequeueReusableCellWithIdentifier. So the cell height is changing inside my tableView's cellForRowAtIndexPath function. I would just like to get rid of this UIView-Encapsulated-Layout-Height constraint because it doesn't seem to be needed, since the rest of my contraints will lead to the same height. It's just preventing me from updating a reuseable cell's height.

@Ortwin Gentz 2015-02-17 17:01:33

Don't fight the framework. UIView-Encapsulated-Layout-Height is automatically added by UITableView. Since this constraint always has a priority of 1000 your vertical constraints should have a lower priority to avoid a conflict.

@Rog 2015-03-18 12:15:15

I maintain that's avoiding conflict not fixing the actual problem - although as this still feels like an Apple bug, I think it's probably the right thing to do. Especially in light of the fact that Apple recalculate this constraint and everything does lay out correctly after the error prints.

@ray 2015-06-01 22:00:27

-1 for this answer since it assumes the constraint being added is correct. The added UIView-Encapsulated-Layout-Width in my case is just wrong, but it seems to be preferred over my explicit constraints at runtime.

@Ortwin Gentz 2015-06-03 19:08:18

@ray See my updated answer. Don't fight the framework and simply accept that the system constraint takes precedence. If you think the system constraint is wrong, make sure to return the correct rowHeight from the delegate.

@Andres 2015-08-06 14:49:32

i love you!!!!!

@TigerCoding 2015-08-27 22:12:21

I think what is happening is the system uses autoresizing mask constraints for a cell content view. So if you specifically set self.translatesAutoresizingMaskIntoConstraints = NO, you interfere with the layout system. Then make sure any constraint changes are done within begin and end updates, and that at least one constraint is 999 as recommended for each axis.

@Fattie 2015-09-23 13:59:37

Hi @OrtwinGentz, thanks for the AAA tip. I had a situation where, I was using native select on rows, and then in the cell using setSelected (NOT as is more usual in the view controller) I was expanding or contracting a UILabel by a few lines. In that case the situation is exactly as you describe, as far as I can see. Can't thank you enough. RogerNolan I suspect that the subtle difference you bring to the fore (and I want it explained, too), depends on the situation. Note all readers you must use THIS INCREDIBLE TIP to resolve these issues.

@Fattie 2015-09-24 16:42:29

a tip so good I sent a bounty, @OrtwinGentz !

@Ortwin Gentz 2015-09-24 16:43:57

Thanks @JoeBlow and I'm glad you find it useful!

@frangulyan 2016-02-11 00:19:02

@RogerNolan this answer is not a fix for the problem but rather a dirty workaround. You should not adjust the priorities of your constraints just because there is something else created. Apple docs say that only prio 1000 is required, when you put a lower number then you eventually ask a different layout from the system. And the advice "don't fight the framework" is at least strange. The solution is simple - you just need to provide "estimatedHeightForRowAtIndexPath". In my case making it bigger than the actual height worked. More precisely, return maximum height for your cell's current state.

@NiñoScript 2016-02-24 19:35:39

I can confirm that making my constraint's priority be UILayoutPriorityRequired-1 (999) and calling tableView.beginUpdates(); tableView.endUpdates() does the trick.

@Tjalsma 2016-05-29 19:25:37

Here it is a year later, and Apple has not clarified or corrected this cluster-code. Every developer who tries to make a custom keyboard runs into this seemingly simple issue.

@Mahesh Agrawal 2016-11-17 18:41:03

this worked for me like magic. thank you so much.

@Andrey Chernukha 2017-12-25 18:19:23

you're a magician

@Aaron Kreipe 2019-04-20 22:29:43

I was getting layout errors while deleting a cell, my layout looks fine and no errors at any other times. Dropping the priority to 999 fixed it.

@Jeff Bowen 2014-11-21 20:23:34

I was able to get the warning to go away by specifying a priority on one of the values in the constraint the warning messages says it had to break (below "Will attempt to recover by breaking constraint"). It appears that as long as I set the priority to something greater than 49, the warning goes away.

For me this meant changing my constraint the warning said it attempted to break:

@"V:|[contentLabel]-[quoteeLabel]|"

to:

@"V:|[email protected][contentLabel]-[quoteeLabel]|"

In fact, I can add a priority to any of the elements of that constraint and it will work. It doesn't seem to matter which one. My cells end up the proper height and the warning is not displayed. Roger, for your example, try adding @500 right after the 388 height value constraint (e.g. [email protected]).

I'm not entirely sure why this works but I've done a little investigating. In the NSLayoutPriority enum, it appears that the NSLayoutPriorityFittingSizeCompression priority level is 50. The documentation for that priority level says:

When you send a fittingSize message to a view, the smallest size that is large enough for the view's contents is computed. This is the priority level with which the view wants to be as small as possible in that computation. It's quite low. It is generally not appropriate to make a constraint at exactly this priority. You want to be higher or lower.

The documentation for the referenced fittingSize message reads:

The minimum size of the view that satisfies the constraints it holds. (read-only)

AppKit sets this property to the best size available for the view, considering all of the constraints it and its subviews hold and satisfying a preference to make the view as small as possible. The size values in this property are never negative.

I haven't dug beyond that but it does seem to make sense that this has something to do with where the problem lies.

@Rog 2014-11-24 08:50:28

Thatks Jeff. Still feels like a bug to me. Apple have not responsed to the rdar though :-(

@Cullen SUN 2014-10-16 17:46:48

Another possibility:

If you using auto layout to calculate the cell height (contentView's height, most of the time as below), and if you have uitableview separator, you need to add the separator height, in order to return for the cell height. Once you get the correct height, you won't have that autolayout warning.

- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
   [sizingCell setNeedsLayout];
   [sizingCell layoutIfNeeded];
   CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
   return size.height; // should + 1 here if my uitableviewseparatorstyle is not none

}

@Leo 2018-04-28 15:44:44

That did help me in a case where I got layout height ambiguities in a pretty complex cell layout only in some simulators (iPad 6 Plus). It appears to me that due to some internal rounding errors the content is a bit squeezed and if the constraints are not prepared to be squeezed than I got ambiguities. So instead of returning UITableViewAutomaticDimension in heightForRowAtIndexPath I return [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize] + 0.1

@Leo 2018-04-28 15:52:12

I mean of course [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].h‌​eight + 0.1

@Kim André Sand 2014-10-01 13:23:09

Sizing the text view to fit its content, and updating the height constraint constant to the resulting height, fixed the UIView-Encapsulated-Layout-Height constraint conflict for me, e.g.:

[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;

@stuckj 2016-03-18 20:10:58

Where did you do this? In layoutSubviews?

@Austin 2014-08-08 16:35:28

Instead of informing the table view to update its constraints, try reloading the cell:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

UIView-Encapsulated-Layout-Height is probably the height the table view calculated for the cell during the initial load, based on the cell's constraints at that time.

@Rog 2014-08-13 09:53:15

I should have stated in my question, I've tried this and it doesn't work. I agree on your guess about UIView-Encapsulated-Layout-Height

@Rog 2014-08-13 09:56:42

Have the bounty anyway for at least submitting an answer. Seems like SO will just let it evaporate otherwise.

Related Questions

Sponsored Content

1 Answered Questions

1 Answered Questions

1 Answered Questions

1 Answered Questions

[SOLVED] UIView-Encapsulated-Layout-Width / Height Constrained to Zero

1 Answered Questions

1 Answered Questions

UITableViewCell expanding autolayout

1 Answered Questions

Sponsored Content