I'm using UIView+Autolayout, to make code-based Auto Layout constraint creation easier, but I'm having trouble adding a constraint to a UITableViewCell sub class.
I have a subview ("viewUser") in the cell to group contact information about who posted the other content being shown in the cell. "viewUser" is a fixed height and is always at the bottom of the cell:
To achieve this in the init method I create "viewUser" and add it to the cell's contentView:
self.viewUser = [UIView newAutoLayoutView];
[self.contentView addSubview:self.viewUser];
and in the updateConstraints method I add the following constraints:
[self.viewUser autoSetDimension:ALDimensionHeight toSize:kContentHeight];
[self.viewUser autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0.0f];
[self.viewUser autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:0.0f];
[self.viewUser autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:0.0f];
[self.viewUser autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0.0f];
When I then create the cells, I get the following warning in the console:
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) (
<NSAutoresizingMaskLayoutConstraint:0xb2a6b40 h=--& v=--& V:[UITableViewCellContentView:0xb289e20(44)]>",
"<NSLayoutConstraint:0xb2a3360 V:[UIView:0xb2a5fd0(53)]>",
"<NSLayoutConstraint:0xb2a3420 V:|-(0)-[UIView:0xb2a5fd0] (Names: '|':UITableViewCellContentView:0xb289e20 )>",
"<NSLayoutConstraint:0xb2a3840 UIView:0xb2a5fd0.bottom == UITableViewCellContentView:0xb289e20.bottom>" )
Will attempt to recover by breaking constraint <NSLayoutConstraint:0xb2a3360 V:[UIView:0xb2a5fd0(53)]>
Which to me makes sense because I'm saying that "viewUser" should have a fixed height and then I pin it to the contentView's edges however if I remove:
[self.viewUser autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0.0f];
When I work out the cell's dynamic height in my tableview controller, its height comes back as 0.
I can't work out what I'm doing wrong and how I remove this warning and get the cell's height, any insights would be great.
It's difficult to diagnose you're problem without knowing the entire layout for your cell. Most likely, you're returning the incorrect height in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
The height you're returning is too small for both "viewUser" and the content subview above.
If you're "viewUser" is disappearing, there are two scenarios I can think of right now that can result from your table cell height being too small.:
You're height constraint for viewUser was broken automatically at
runtime due to constraint conflicts. This is what happens in the
warning you posted: cell height was too small (44) for your viewUser
height (53)
You're viewUser's height is superseded by a higher priority
constraint possibly from the content view. This is less unlikely but is also a possibility.
Related
I have a simple UICollectionVewController with one prototype cell. In the cell all I have is a UILabel.
I have size classes and auto layout selected. I have set a constraint for
top (Top Space to cell): = 100
Height >= 17
Trailing space to Cell = 0
Leading Space to Cell = 0
In this configuration it works fine. However I want a left and right margin on the cell, so I changed the Leading and Trailing space to 10.
Although this works, I get the following error:
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:0x1740907c0 H:[UILabel:0x14de1a380'This is a test to see wha...']-(10)-| (Names: '|':UIView:0x14de1a270 )>",
"<NSLayoutConstraint:0x174090810 H:|-(10)-[UILabel:0x14de1a380'This is a test to see wha...'] (Names: '|':UIView:0x14de1a270 )>",
"<NSAutoresizingMaskLayoutConstraint:0x174090d10 h=--& v=--& H:[UIView:0x14de1a270(0)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x1740907c0 H:[UILabel:0x14de1a380'This is a test to see wha...']-(10)-| (Names: '|':UIView:0x14de1a270 )>
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.
If I set the leading space to 10 and remove the training space and set a width constraint, I do not get the error.
However, the width of the cell is variable so I can't set the width.
I can work around it, by creating an outlet to the width constraint and doing this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
CGRect colViewSize = collectionView.bounds;
int width = colViewSize.size.width;
width -= 28; // 10px either size and 8px in middle
width = width/2;
width -=14; // margin of 7 (x 2)
cell.labelconstraintWidth.constant = width;
return cell;
however this seems such a hack.
Does anyone have any clue what is happening here.
Simply put, your view resizes, and when it does, the constraints of 10 leading and 10 trailing space can't be satisfied because your view's width is less than 20.
Solution? Either put a percentual label margins / width to the superview or make the the margins zero, if you don't want to think about it.
However, if you have a encapsulated logic that calculates the cell width, you can easily add margin constraints from code.
EDIT:
You might have an autolayout constraint that's 50%, but that's not the issue here. You got an AutoResizingMaskLayoutConstraint that's affecting you view's width.
In order to create a percentual margin in storyboard, do:
1) Select your view in the view hierarchy
2) Hold CTRL and drag to your superview(or the root view that's taking the full bounds of the superview)
3) Select Horizontal Spacing
4) Select your view and double click on the Horizontal Spacing constraint
5) In the size inspector select:
First item: YourView.Leading
Relation: Equal
Second item: YourSuperview.Trailing
Constant: 0
Priority: 1000
Multiplier: (your margin percentage)
As #Bamsworld said, you should double check for AutoResizing masks. They might be conflicting with your AutoLayout constraints.
The error tells you one or more constraints is not needed and lists one of the constraints as NSAutoresizingMaskLayoutConstraint. It would appear that there is a conflict with auto-layout and autoresizingmasks. I suggest to use one or the other.
Try removing the autoresize-constraint by calling setTranslatesAutoresizingMaskIntoConstraints on the view and pass in NO. Also set the autoresizingMask property of the view to UIViewAutoresizingNone (just to be sure).
I have a UIView in a static UITableViewCell. I added a height constraint to the view. I think, the correct way to animate the change of a views height would be like this:
self.myViewsHeightConstraint.constant = 100; // Coming from 0 and vice versa
[UIView animateWithDuration:0.3f animations:^{
[self.view layoutIfNeeded];
}];
I set the tableView row height to:
tableView.estimatedRowHeight = 40;
self.tableView.rowHeight = UITableViewAutomaticDimension;
Now I need to update the cells height. So I think a begin/endUpdate would be proper. When the veiw and the cell animate its heights change, they aren't synchronized, so because they don't animate the exact same time, I get the following warning:
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:0x7f8d62d15670 V:[tagResultTableView:0x7f8d62d901a0(107)]>",
"<NSLayoutConstraint:0x7f8d62cea010 tagResultTableView:0x7f8d62d901a0.top == UITableViewCellContentView:0x7f8d62d8fd50.topMargin - 8>",
"<NSLayoutConstraint:0x7f8d62cf0760 V:[tagResultTableView:0x7f8d62d901a0]-(8)-[UILabel:0x7f8d62d2e2e0'Limit: Up to 5 tags']>",
"<NSLayoutConstraint:0x7f8d62cf0800 UILabel:0x7f8d62d2e2e0'Limit: Up to 5 tags'.bottom == UITableViewCellContentView:0x7f8d62d8fd50.bottomMargin>",
"<NSLayoutConstraint:0x7f8d62f726d0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8d62d8fd50(32)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f8d62d15670 V:[tagResultTableView:0x7f8d62d901a0(107)]>
If my logic is correct, how can I animate them the exact same time. Or how can I animate them so I won't get the error?
If you want to make sure the Cell stays static and not change implement this method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{}
And have a return statement inside that returns the final value of the cell height.
You have the height sizing function there, I don't know your exact wants, but if I wanted to change heights I would add an if statement inside the function to test if it's the same as the index as the item you want to make bigger. Refresh your list which will resize the element without causing other issues. I'd also ditch the constraint on the cell view. I don't use size constraints where I plan on resizing the element.
There's been a lot of questions about how to make dynamic cell height using Autolayout and TextView inside it. Here's the story
I follow this article iOS dynamic table view cells with varying row height and Autolayout. In this case, we replace the 2nd label in the article with a TextView, with the same set of constraints
The TextView does not have intrinsic content size as the Label. So we must use sizeThatFits and creating height constraint on the TextView, like this.
This height constraint is an IBOutlet from the Nib
ViewController.m
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
Item *item = self.dataSource.items[indexPath.row];
[self.prototypeCell configureWithModel:item];
[self.prototypeCell updateConstraintsIfNeeded];
[self.prototypeCell layoutIfNeeded];
self.prototypeCell.textViewHeightConstraint.constant = [self.prototypeCell.textView sizeThatFits:CGSizeMake(self.prototypeCell.frame.size.width, CGFLOAT_MAX)].height;
[self.prototypeCell updateConstraintsIfNeeded];
[self.prototypeCell layoutIfNeeded];
return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}
CustomCell.m
- (void)configureWithModel:(Item *)model {
self.textView.text = model.content;
}
I then see in the console that
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)
(
"",
"",
"",
"",
""
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fa3e3988a90 UITextView:0x7fa3e210f000.height == 200>
Here you can see that the Autolayout system removes the height constraint on the TextView.
The problem here is that we update the height constraint on the TextView, but the contentView of the cell seems to ignore this.
I know the key to this dynamic height is that the subviews (Label, TextView) must determine its own size (Label has its own intrinsic content size, for TextView we manually set its height constraint) so that the contentSize is then calculated
What am I missing?
Simply lowering the priority of the textViewHeightConstraint (below 1000) fixes the Unable to simultaneously satisfy constraints problem
I have cell with only one button in it, I've added height of button = 30, top = 10 and bottom = 10 constraints, so systemLayoutSizeFittingSize must return height of cell = 50. Nevertheless it returns 50.5 and I see in the log :
Will attempt to recover by breaking constraint
I'm using grouped table view and separator set to none. Where it takes extra 0.5px?
code snippet:
[cell layoutSubviews];
return [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;// 50;
log:
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:0x7ffa71e273e0 V:[UIButton:0x7ffa71e24900'Book a new hotel'(30)]>",
"<NSLayoutConstraint:0x7ffa71e20bc0 V:|-(10)-[UIButton:0x7ffa71e24900'Book a new hotel'] (Names: '|':UITableViewCellContentView:0x7ffa71e06030 )>",
"<NSLayoutConstraint:0x7ffa71e23ae0 UITableViewCellContentView:0x7ffa71e06030.bottomMargin == UIButton:0x7ffa71e24900'Book a new hotel'.bottom + 2>",
"<NSLayoutConstraint:0x7ffa705f6640 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7ffa71e06030(50.5)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7ffa71e273e0 V:[UIButton:0x7ffa71e24900'Book a new hotel'(30)]>
iOS 8
If your target is iOS8 then it makes this much easier as table view cells can now be sized automagically by AutoLayout.
To do this just return UITableViewAutomaticDimension in your heightForRow method...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
Essentially this does what I think you are trying to do with your code snippet.
iOS 7
For iOS 7 I'd set out the constraints differently. Instead of setting the top and bottom constraints I'd just have a "centre vertically" constraint.
Then you can return any number you want to return and the constraints won't break.
You don't get the automatic height thing but the height doesn't appear to change anyway.
I've got a UITableViewCell which contains a UIWebView. The table view cell adjusts it's height depending on the web view contents.
I've got it all working fine, however when the view loads, I get a constraint violation exception in the debugger (the app continues running and functionally works fine, but I'd like to resolve this exception if possible).
How I've got it set up:
The TableView sets the cell height like this:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0) {
[_topCell layoutIfNeeded];
CGFloat finalHeight = [_topCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return finalHeight + 1;
}
The cell constraints are as follows:
Arbitrary 7px offset from the cell's contentView (top) to the webView
Web view has arbitrary fixed height constraint of 62px (will expand later once content loads)
Arbitrary 8px offset from the webView to the cell's contentView (bottom)
in my viewDidLoad, I tell the webView to go and load a URL, and in the webViewDidFinishLoad, I update the web view height constraint, like this
-(void)webViewDidFinishLoad:(UIWebView *)webView {
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
// fittingSize is approx 500
[self.tableView beginUpdates];
// Exceptions happen on the following line setting the constant
_topCell.webViewHeightConstraint.constant = fittingSize.height;
[_topCell layoutSubviews];
[self.tableView endUpdates];
}
The exception looks like this:
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:0x10964b250 V:[webView(62)] (Names: webView:0x109664a00 )>",
"<NSLayoutConstraint:0x109243d30 V:|-(7)-[webView] (Names: webView:0x109664a00, cellContent:0x1092436f0, '|':cellContent:0x1092436f0 )>",
"<NSLayoutConstraint:0x109243f80 V:[webView]-(8)-| (Names: cellContent:0x1092436f0, webView:0x109664a00, '|':cellContent:0x1092436f0 )>",
"<NSAutoresizingMaskLayoutConstraint:0x10967c210 h=--& v=--& V:[cellContent(78)] (Names: cellContent:0x1092436f0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x10964b250 V:[webView(62)] (Names: webView:0x109664a00 )>
This seems a bit weird. It's implied that the constraint which sets the height of the web view is going to be broken, however the web view does get it's height correctly set, and the tableview renders perfectly well.
From my guesses, it looks like the newly increased web view height constraint (it's about 500px after the web view loads) is going to conflict with the <NSAutoresizingMaskLayoutConstraint:0x10967c210 h=--& v=--& V:[cellContent(78)] setting the cell height to 78 (put there by interface builder). This makes sense, however I don't want that cell content to have a fixed height of 78px, I want it to increase it's height, and functionally, it actually does this, just with these exceptions.
I've tried setting _topCell.contentView.translatesAutoresizingMaskIntoConstraints = NO; to attempt to remove the NSAutoresizingMaskLayoutConstraint - this stops the exceptions, but then all the other layout is screwed up and the web view is about 10px high in the middle of the table view for no reason.
I've also tried setting _topCell.contentView.autoresizingMask |= UIViewAutoresizingFlexibleHeight; in the viewDidLoad to hopefully affect the contentView 78px height constraint, but this has no effect
Any help would be much appreciated
An alternative answer, which ends up being far simpler:
Set the priority of the webViewHeight constraint to something other than required. Works well and there are no warnings. I'd recommend going with this :-)
I worked out a solution. It seems like a hack, but it works correctly and produces no errors and warnings.
The trick was to dissociate the web view's layout and height from the contentView.
I did this as follows:
In the tableviewcell, add a new UIView, called containerView
Give it constraints for Top:0 Left:0 Right:0 but no bottom constraint and no height constraint
Put the web view (and any other content) inside this containerView
Add constraints such that the web view is 7px from the top and bottom of containerView, and has a height constraint of 62px
At this point, the containerView isn't connected to the bottom of the table view cell, and so it could over/under flow without auto-layout complaining.
Now, in tableView:heightForRowAtIndexPath:, we calculate the cell height based on the containerView as follows:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0) {
CGFloat finalHeight = [_topCell.containerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return finalHeight + 1;
}
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
All done!
From the Constraint log
The Constraint you are trying to achieve is as below,
In a 78 px cellContentHeight you want to fit a content of 7(top space) + 62 (Web View) + 8 (bottom space). which is 77 (not equal to 78). Either of two option would work,
Try giving top margin as 8 (instead of 7) and make it 78. (V:|-8-[WebView(62)]-8-|)
Attach to the top, give 8 pixel top space (V:|-8-[WebView(62)]) and don't specify 8 px bottom space.
Attach to Bottom, give 8 pixel bottom space (V:[WebView(62)-8-|) and don't specify top space.