UITableView dynamic cell height conflict - height NOT depending on a label - ios

So, my table view displays images. Every cell is basically an image filling out the cells entire contentView. As the images come with different aspect ratios, I need my cells to be adjusting their height depending on the table views width and the aspect ratio of the image. I've followed this Ray Wenderlich tutorial but now get a constraint conflict. The image is resized by altering the imageViews height constraint e.g. myImageViewHeight.constant = tableView.frame.width / aspectRatio
2016-06-16 13:56:25.823 MyApp[92709:5649464] 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.
(
"<NSLayoutConstraint:0x7fbcdaf5c190 V:[UIImageView:0x7fbcdaf5c300(303)]>",
"<NSLayoutConstraint:0x7fbcdaf5b460 V:|-(0)-[UIImageView:0x7fbcdaf5c300] (Names: '|':UITableViewCellContentView:0x7fbcdaf52230 )>",
"<NSLayoutConstraint:0x7fbcdaf5b4b0 V:[UIImageView:0x7fbcdaf5c300]-(0)-| (Names: '|':UITableViewCellContentView:0x7fbcdaf52230 )>",
"<NSLayoutConstraint:0x7fbcdaf5e550 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fbcdaf52230(100)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fbcdaf5c190 V:[UIImageView:0x7fbcdaf5c300(303)]>
The image view has the following constraints and has the cells contentView as superview.
In the table view controller class, I'm using
self.tableView.estimatedRowHeight = 80
self.tableView.rowHeight = UITableViewAutomaticDimension
I've also tried this - same result.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
I'm guessing that I have to get rid of the 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fbcdaf52230(100)]>" but how? Also, does anybody know some kind of documentation/tutorials on how to properly create table views with dynamic content that not only cover having a bunch of labels inside the cell? My code works just fine if the image view is replaced by a label...

You implementation is correct don't change anything in your code or any of your constraints.Removing the warning is easy just select the height constraint and lower its priority from 1000 to 999 it will fix your warning.If it still comes, let me know.

It is an old question but I put this just for the record.
The problem here is the estimated hight, 80, that is lower then the computed hight, 100.
Changing the estimated hight to >=100 should fix this warning.

Related

IOS - How to remove NSAutoresizingMaskLayoutConstraint from a view

I am having issues getting rid of a pesky NSAutoresizingMaskLayoutConstraint that is breaking the auto-layout width constraints of some UITextView.
I have a UITableviewCell that contains a text view, and the text view's height is dynamically set based on the content of the text view's text using
self.table.sectionHeaderHeight = UITableView.automaticDimension;
self.table.estimatedSectionHeaderHeight = 200;
This works pretty well when auto-layout is used (I am setting my constraints via the interface and not code). However, I noticed that the widths of the text views are conflicting with NSAutoresizingMaskLayoutConstraint - and the end result is the width constraint on my text view is being removed.
[LayoutConstraints] 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)
(
"<NSAutoresizingMaskLayoutConstraint:0x6000012ff070 h=--& v=--& UITextView:0x7fde2f09e600'At teenfvfed'.width == 89.5 (active)>",
"<NSLayoutConstraint:0x600001292210 UITextView:0x7fde2f09e600'At teenfvfed'.width == 293 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600001292210 UITextView:0x7fde2f09e600'At teenfvfed'.width == 293 (active)>
The common answer I see online is to add translatesAutoresizingMaskIntoConstraints = false on the text view, but this doesn't have any effect and NSAutoresizingMaskLayoutConstraint remains. Any help would be greatly appreciated to remove this!
Try this:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.text.translatesAutoresizingMaskIntoConstraints = false
cell.setNeedsLayout()
cell.layoutIfNeeded()
}

The constraints in my tableview isn't working on my view objects. Any reason why?

I have downloaded data from a URL and wanted to display it in a table view. I've gotten the data to populate, but I can get the constraints to work on the view objects in my table view.
Here is a complete picture of my storyboard :
But this is what I end up getting in my simulator :
Does anyone understand the reason why I am getting these constraint problems? Thank you.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:"PictureCell") as! PictureCellTableViewCell
cell.likeLabel.text = String(pictures[indexPath.row].likes)
cell.tagsLabel.text = pictures[indexPath.row].tags
return cell
}
Here's how to resolve the constraint issues you're having. Starting from the beginning....
Step 1: Add labels to cell.
Step 2: Embed labels in stack view.
Step 3: Pin all edges of stack view to cell.
Step 4: Almost there, we just need to resolve content hugging priority issues with labels...
]
Step 5: Lower the bottom label's Vertical Content Hugging Priority to be a smaller value than the top label. With this in place, both labels won't have to fight over who should be prioritized. The bottom label now has a lower vertical content hugging priority so it will allow itself to adjust its vertical height. In other words, the label Because of how AutoLayout works and cell resizing, this is required.
Using storyboards as an intro-iOS developer is a good starting point to visualize constraints, as constraint issues can be difficult to resolve in UIKit when done programatically.
As you progress in your understandings though you'll eventually move away from storyboards all together and layout your views programmatically.
You could also opt to learn SwiftUI and see how dealing with constraints is not as an issue as in UIKit.

SnapKit and Dynamic UITableViewCell not being laid out correctly

I'm currently building a reusable view that takes advantage of the dynamic resizing that UITableView has on offer with its cells.
What I'm trying to achieve is to give a view within the cell a width and a height depending on the size it has been given within the size property that is defined within a struct, I pass this through using a configure function, after setting the size of the view.
I then center it on the xAxis and apply any margins around the view using a property within the struct which is just a UIEdgeInset property. Using the SnapKit library this looks like this.
if let formImageItem = formImageItem,
let size = formImageItem.size {
formItemImgVw = UIImageView(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
formItemImgVw?.image = formImageItem.image
formItemImgVw?.contentMode = formImageItem.aspectFit
contentView.addSubview(formItemImgVw!)
formItemImgVw?.snp.makeConstraints({ (make) in
make.size.equalTo(CGSize(width: size.width, height: size.height))
make.top.equalTo(formImageItem.margin.top)
make.bottom.equalTo(formImageItem.margin.bottom)
make.centerX.equalToSuperview()
})
}
The issue I seem to be having is i'm getting the following warning.
2018-10-12 14:57:34.101532+0100 xxxx Dev[6104:2160548] [LayoutConstraints] 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.
(
"<SnapKit.LayoutConstraint:0x2830ec720#FormImageViewTableViewCell.swift#61 UIImageView:0x10530a940.height == 114.0>",
"<SnapKit.LayoutConstraint:0x2830ec780#FormImageViewTableViewCell.swift#62 UIImageView:0x10530a940.top == UITableViewCellContentView:0x105503500.top>",
"<SnapKit.LayoutConstraint:0x2830ec7e0#FormImageViewTableViewCell.swift#63 UIImageView:0x10530a940.bottom == UITableViewCellContentView:0x105503500.bottom>",
"<NSLayoutConstraint:0x2837e2580 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x105503500.height == 44 (active)>"
)
Will attempt to recover by breaking constraint
<SnapKit.LayoutConstraint:0x2830ec720#FormImageViewTableViewCell.swift#61 UIImageView:0x10530a940.height == 114.0>
I understand what this is telling me basically that it's choosing to use the height 44 that is set by default for a table view cell. But I can't seem to get my head around why it's using this default height rather than the one that I have defined within my constraints and applying the height to the view whilst also adding the spacing to the top and bottom.
Also when using the debugger to inspect the UI it seems like a height isn't being set at all and as you can see the height of 44 is just clipping the entire imageview.
So ultimately what I'm trying to achieve is the ability to give a view a defined height and width, and use values to apply top and bottom spacing(margins) in order to resize the cell that it's currently within.
You need to lower the priority of the bottom constraints so replace this
make.bottom.equalTo(formImageItem.margin.bottom)
with
make.bottom.equalTo(formImageItem.margin.bottom).priority(999)
plus in viewDidLoad
tableView.estimatedRowHeight = 120
tableView.rowHeight = UITableViewAutomaticDimension
abd don't implement heightForRowAt

How do I support self sizing cells on iOS7?

With the release of iOS8 I have designed my table view with cells taking advantage of self sizing cells. But I need my tables to work in iOS7 as well. How do I do that? Is there a way to check whether self sizing cells is supported or not in runtime, or can I implement some table delegate methods in my controller which will not be called in iOS7?
If I try my table with self sizing cells in iOS7 I get errors on the console 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:0x7fc912d1d5a0 V:|-(>=11)-[UILabel:0x7fc912d13900] (Names: '|':UITableViewCellContentView:0x7fc912d13400 )>",
"<NSLayoutConstraint:0x7fc912d1d6b0 V:[UILabel:0x7fc912d13900]-(11)-| (Names: '|':UITableViewCellContentView:0x7fc912d13400 )>",
"<NSAutoresizingMaskLayoutConstraint:0x7fc912d24d80 h=--& v=--& V:[UITableViewCellContentView:0x7fc912d13400(0.5)]>"
)
This is the solution I have found thus far, but it requires checking for specific version number rather than capability. You only set UITableViewAutomaticDimension if you have iOS 8 or higher as version:
override func viewDidLoad() {
if NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1 {
self.tableView.estimatedRowHeight = 80
self.tableView.rowHeight = UITableViewAutomaticDimension
}
}
For iOS 7 you need to calculate a height for each cell. But if you are on iOS 8 you can return UITableViewAutomaticDimension as the height:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1 {
return UITableViewAutomaticDimension
}
return 50 // Or whatever calculated value you need for cell height
}
For iOS versions older than 8.0, you can always write the usual heightForRowAtIndexPath method where you create a cell, run an auto layout pass on it and then return the actual height.

UITableViewCell rounding error with NSAutoresizingMaskLayoutConstraint, but size correctly set in Storyboard AND heightForRowAtIndexPath:

I am trying to use AutoLayout to configure the subviews in my table view cells and in the ideal case would like the table view cell to be just as high as necessary to contain all the subviews. However, that does not seems to be possible, since the height for a cell is determined before the cell is actually created.
So, for now, I just looked at the constraints I set up and calculated the total height needed to contain everything and set that in the storyboard as the row height for this specific TableViewCell prototype cell and I also return this height in the tableView:heightForRowAtIndexPath: method.
Now, my table view cell looks like this (screenshot from storyboard):
There are two constraints which don't have a size shown, they both have the size 10 (the button-top-container-view and the distance between the slider and the label in the middle).
Going from top-to-bottom the following distance occur:
10 (distance)
50 (height of button)
20 (distance)
20 (height of label)
10 (distance)
30 (height of slider)
20 (distance)
leading to a total height of 160.
That's exactly what I have set in the inspector of this cell:
but as you can see from the first screenshot, Interface builder complains that the constraints are conflicting (that's why they are displayed in red). IB is satisfied if I set the height to 161, but that is wrong.
Also, if I do, I get exceptions at runtime due to conflicting constraints:
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:0x8d80240 V:[UISlider:0x8d81190(30)]>",
"<NSLayoutConstraint:0x8d833e0 V:[UILabel:0x8d83300(20)]>",
"<NSLayoutConstraint:0x8d83750 V:[UIButton:0x8d83600(50)]>",
"<NSLayoutConstraint:0x8d85050 V:|-(10)-[UIButton:0x8d83600] (Names: '|':UITableViewCellContentView:0x8d80ef0 )>",
"<NSLayoutConstraint:0x8d851d0 V:[UILabel:0x8d83300]-(10)-[UISlider:0x8d81190]>",
"<NSLayoutConstraint:0x8d85200 V:[UISlider:0x8d81190]-(20)-| (Names: '|':UITableViewCellContentView:0x8d80ef0 )>",
"<NSLayoutConstraint:0x8d85320 V:[UIButton:0x8d83600]-(20)-[UILabel:0x8d83300]>",
"<NSAutoresizingMaskLayoutConstraint:0x8d8b7a0 h=--& v=--& V:[UITableViewCellContentView:0x8d80ef0(161)]>"
)
the last one is the only one I did not explicitly set, but it seems to be generated from the row-height setting in the storyboard, since my tableView:heightForRowAtIndexPath: method returns 160:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == self.fetchedItemSetsController.fetchedObjects.count - 1)
{
return 160;
}
return 44;
}
(at the moment I always want the last cell to be the large and special one)
So, okay, IB complains when I set the row height to 160, but when I set it to 161 the runtime complains. So I tried ignoring IB and set the row height to 160 in the storyboard as well (as seen in the second screenshot). In that case, I get the same error message with a tiny difference:
"<NSLayoutConstraint:0x17809ce80 V:[UISlider:0x125613660(30)]>",
"<NSLayoutConstraint:0x1782814f0 V:[UILabel:0x125615100(20)]>",
"<NSLayoutConstraint:0x178281a90 V:[UIButton:0x125615b50(50)]>",
"<NSLayoutConstraint:0x178281b80 V:|-(10)-[UIButton:0x125615240] (Names: '|':UITableViewCellContentView:0x178161980 )>",
"<NSLayoutConstraint:0x178281c20 UIButton:0x1256157b0.height == UIButton:0x125615240.height>",
"<NSLayoutConstraint:0x178281d10 UIButton:0x1256157b0.height == UIButton:0x125615980.height>",
"<NSLayoutConstraint:0x178281e00 V:[UILabel:0x125615100]-(10)-[UISlider:0x125613660]>",
"<NSLayoutConstraint:0x178281e50 V:[UISlider:0x125613660]-(20)-| (Names: '|':UITableViewCellContentView:0x178161980 )>",
"<NSLayoutConstraint:0x178281f40 UIButton:0x125615980.height == UIButton:0x125615b50.height>",
"<NSLayoutConstraint:0x178282030 V:[UIButton:0x125615240]-(20)-[UILabel:0x125615100]>",
"<NSAutoresizingMaskLayoutConstraint:0x178283340 h=--& v=--& V:[UITableViewCellContentView:0x178161980(159.5)]>"
The constraint for the height of the table view cell is now 159.5 instead of 160. I also encountered it as 160.5 before, with the same storyboard setting, but I am not sure where that came from.
So, first I thought it was just a display bug in IB, but now it actually seems to be creating the wrong constraint. It creates a constraint with 160.5 (or 159.5) instead of the 160 I specified. Why is that? What can I do about it?
Btw: The cells seems to be displayed correctly, but then again I doubt I would be able to see a 0.5 point difference. Mainly, I would like to get rid of the exceptions, since they make the debugging much harder, but I would also like to know what is going on here.
UPDATE:
The 159.5 is not JUST due to the storyboard setting I just noticed, it is a strange combination of the storyboard setting and the return value of the tableView:heightForRowAtIndexPath:.
Here are a few examples of the height of the generated autoresizingmasklayoutconstraint depending on the storyboard height (sb) setting and the return value of the tableView:heightForRowAtIndexPath: method (method). In all cases, the constraints of the subview should lead to a height of 160 and are not changed.
160 (sb) & 160 (method): 159.5 (constraint)
161 (sb) & 160 (method): 159.5 (constraint)
160 (sb) & 161 (method): 160.5 (constraint)
162 (sb) & 161 (method): 162 (constraint)
161 (sb) & 162 (method): 161 (constraint)
162 (sb) & 162 (method): 162 (constraint)
So I thought, okay, for (6.) they all agree, I will just make the label's height 22, so the total height according to the subviews will be 162 as well. Result:
6a. 162 (sb) & 162 (method), 162 (total height of subviews): 161.5 (constraint)
WHAT?!
Any ideas what's happening here?
Update 2
I provided an example project that reproduces the issue on Github.
Separator is messing with the height of the contentView, either it should be disabled (and replaced with custom one if required) so that the contentView height matches the height of the cell or the constraints should be changed to more flexible considering that the contentView height may mismatch the height of the cell. Doing both would be even better.
Now that you've uploaded the example to github, I was able to see that the constraints were in fact over-determined. There was too much "this is equal to that", esp. in the area of vertical spacings/heights. I simplified the constraints, and was able to change to your code to this:
-(CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0)
{
return 44;
} // else
UITableViewCell* cell =
[tableView dequeueReusableCellWithIdentifier:#"LargeCell"];
CGSize sz =
[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingExpandedSize];
return sz.height;
}
If I'm not mistaken, that's exactly the sort of thing you are after, i.e. to let the constraints themselves dictate the height of the cell.

Resources