How can I make self sizing UITableViewCell with a UIStackView inside it - ios

I can't achieve a self sizing UITableViewCell with a UIStackView inside it.
For the UITableViewCell I'm doing something like this:
On code I'm doing this:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 80
}
But when I run the app, the UITableViewCells doesn't resizes:
What do I miss to make the UITableViewCell self sizing with a UIStackView inside it?

The cell resizing is working fine. Your problem is that you set a fixed height for the Stack View.
The View in the horizontal Stack View has its height set to 64, most likely with its standard priority set to 1000. This Stack View most likely has its distribution set to Fill. You basically told the Stack View that the containing image has to exactly fill the Stack View with a height of 64. This is also limiting the Stack View to 64 and with it the vertical Stack View besides the View. Change the distribution of the horizontal Stack View that contains the View to Center if you want the vertical Stack View next to the View to get bigger than 64.

Do you have multi line labels? If so make sure you have the lines property in IB set to 0 not 1.
Pin top and bottoms of stackView.
If that doesn't do it, check these:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}

The trick to getting Auto Layout for self sizing cells to work on a UITableViewCell is to ensure you have constraints to pin each subview on all sides — that is, each subview should have leading, top, trailing and bottom constraints. Then, the intrinsic height of the subviews will be used to dictate the height of each cell.
Here is a nice tutorial that will get you through it.

Try pinning the stack view itself. Remember the stackView has the responsibility to equally space, evenly distribute, etc., so it needs to use constraints between itself and its superView to figure all that out.
Here's an apple explanation.
HTH

open class IntrinsicHeightTableView: UITableView {
open override var contentSize: CGSize {
didSet { invalidateIntrinsicContentSize() }
}
open override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
} }

Related

How to create collectionview with dynamic height as per the content in it?

Souce Code : My code
I need to create layout having dynamic size as per the text of different length, for that using collectionview inside tableview cell
I have created custom collectionview with self sizing collectionview cell in it. I have used CustomFlowLayout(sub class of UICollectionViewLayout) with prepare override method to calculate size of each cell.
I am able to achieve the dynamic size collectionview cell, now I need to make collectionview height dynamic based on its content size, as of now I have taken height contstraint of collectionview and set it as 200 fixed.
Need help regarding how to set collectionview height dynamically based on its content and at the same time adjust the parent tableview cell to fit the its content
Below is the screenshot of what I want to achieve, Thanks much!
In your CustomFlowLayout which is subclass of UICollectionViewLayout you need to override the collectionViewContentSize as shown below.
Calculate your content height & return it.
override public var collectionViewContentSize: CGSize {
return CGSize(width: collectionViewWidth, height: contentHeight)
}
Hope it helps!
Try adjusting your collection view height using
if heightConstraint.constant != collectionView.contentSize.height {
heightConstraint.constant = collectionView.contentSize.height
view.updateConstraintsIfNeeded()
}
And you can set your tableview row height as automatic. if your contraints are proper the tableview cell will automatically adjust its height
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
I have already answered this question here: https://stackoverflow.com/a/53102033/7388630
You can get fully working and explained code sample here

UITableViewAutomaticDimension not working with custom XIB cell

So I created a custom cell with a XIB file and added the constraints: Image. However, the UITextView is not automatically adjusting its height depending on the amount of text inside it.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
this may help you
override func viewDidLoad()
{
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 85
}
and make sure to disable textview scroll and have relation in the top down width hight
this link may help you
https://www.raywenderlich.com/129059/self-sizing-table-view-cells
You have too many vertical constraints such that it fixes the size of your textView. You should remove one of the top constraints, or just use a stackview as the only child of the content view and give the stack leading trailing, top bottom from the content view (in this case you don't need any other constraints).
Follow these steps :
Give constraints to ImageView - Leading, Trailing,Top with contentview and Height Constraint(if you want fix height else) give bottom with textview.
Give constraints to TextView - Leading, Trailing, bottom with contentView .
Disable scroll of textView .
Add Following line in ViewDidLoad.
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44

Dynamic UITableViewCell not resizing according to the content

Senario A:
If I set the label content in cellForRowAtIndexPath, the cell correctly get resized.
Senario B:
If I change the text content in custom action in cell, the cell sized does not get changed.(I do call setNeedsLayout + layoutIfNeeded)
How to fix this?
EDIT:
1) I have set,
myTableView.estimatedRowHeight = 71.0
myTableView.rowHeight = UITableViewAutomaticDimension
2) I have correctly added auto layout constraints.
I was running into this issue, and my problem was that I was constraining the content to self (the UITableViewCell) and not to self.contentView (the contentView OF the cell). Hope this helps someone else who has built their cells all in code!
In my case, the cell's custom size was enabled:
After you change the text of the cell, just reload that particular cell or simply call mainTableView.reloadData().
To reload that cell-
//indexPath is indexPath of cell you just changed label of
mainTableView.reloadRows(at: indexPath, with: .automatic)
In my case, in the same cell I had an imageView in the top left corner with a "center vertically in container" constraint, and a "top space container" constraint.
Obviously to satisfy this two constraint the cell must have an height equal to:
(height of the imageView / 2) + (length of the top space container constraint).
This height is not enough to fit the label text, so my label had only 1 line visible.
After I have deleted the imageView top constraint all went to the right place, in my case i wanted the image to be centered, if the image had to stay in the top left corner I had to take off the "center vertically in container" constraint.
I hope this can help someone.
First of all, I don't specifically know what was your action on UITableViewCell. So, I assume I do that in UITableViewCell selection.
The below answer only work on iOS 9 and above
But, for some reason, it failed to do it in iOS 8 until it scroll. So, I will update the answer for iOS 8.
I have seen you have used UITableView's estimatedRowHeight and rowHeight at your project. So,
Please check the following
Make sure UITableView's estimatedRowHeight and rowHeight include inside viewDidLoad()
Make sure your UILabel lines set to 0
Make sure there is no constraints about height for your UILabel and let the constraints be like that :
If there are other component also included, make sure only bottom and top space constraints included or top space to container margin and bottom space to container margin.
Every time that you want to update the cell, you have to reload tableView no matter what your current situation will be.
So, don't say anything yet before you try this sample project, #Rikh answer still work. May be you are going in wrong direction. Here's the solution. Please do as I said steps by steps and let me know if that didn't work out. You might need to share your sample project which is causing.
Sample Demo - DynamicCellDemo
UPDATE for iOS 8 : update the following code for iOS 8 users
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 9, *) {
// do nothing
} else {
tblDynamic.reloadData()
}
}
what you can do is set the AutoLayout constraints for the label in present in the cell from all the sides that is from Top, Bottom, Leading and Trailing. Then add the following UITableViewDelegate method to your class.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 500
}
This will do the job. As now content in table view cell automatically adjusts the height of the cell.
Try this if it works:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Add the following in your viewDidLoad()
-(void)viewDidLoad{
[super viewDidLoad];
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
}

Table view cell size for horizontal UICollectionView within a UITableView

I have set up multiple horizontally-scrolling UICollectionView(s), within a UITableView, so I can scroll vertically.
One issue I ran into is that, the row's height for the table view, is called before I know how tall they should be (I want the whole height of each UICollectionView to fill the table view cell).
How do I go about solving this?
My suggestion would be to create a custom layout for the collection view. Then it could compute the cell size and be notified when the collection view is resized.
Using this function you can determine the height of the collectionView you have.
Call the collectionViewContentSize on your aCollectionView.collectionViewLayout property and get the height and width of the content in form of CGSize.
OR
-(CGFloat)collectionViewHeight
{
[collectionView layoutIfNeeded];
return [collectionView contentSize].height;
}
Create a variable for your height, and return that variable in your heightForRowAtIndexPath method. Then reload your tableView once you have figured out your height.
var height: CGFloat = 44
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return height
}
func something() {
height = 1234//Or whatever your height should be
self.table.reloadData()
}

Settings UITableViewCell Heights With Multiline UIButtons in Auto Layout

I have a UITableView with custom UITableViewCells, each has a UIButton inside. I'm setting buttons' titles from an array, so the size of the buttons change according to the title. I need to return correct height based on the inner button's size in heightForRowAtIndexPath event.
Since I'm using auto layout, I've created an outlet for the button's height constraint and I'm updating it in the cell's layoutSubviews() event like this:
class CustomCell: UITableViewCell {
/* ... */
override func layoutSubviews() {
super.layoutSubviews()
self.myButton?.layoutIfNeeded()
self.heightConstraint?.constant = self.myButton!.titleLabel!.frame.size.height
}
}
Then I return the height based on the button height and top-bottom margins like so:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomCell
cell.myButton?.setTitle(self.data[indexPath.row], forState: UIControlState.Normal)
cell.bounds = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds))
cell.setNeedsLayout()
cell.layoutIfNeeded()
return cell.myButton!.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + (cell.topMarginConstraint!.constant * 2) /* top-bottom margins */ + 1 /* separator height */
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomCell
cell.myButton?.setTitle(self.data[indexPath.row], forState: UIControlState.Normal)
return cell
}
On the first launch, there seems to be no problem. However, after I begin scrolling, then the height of some rows seem to be mistaken. When I get back to the top, I see that previous cell heights get to be broken as well.
When I googled for similar problems, issue seems to be about reusable cells, though I was unable to find another way to calculate the height. What can be done to reuse cells correctly or getting the correct height, perhaps by another method?
More info and source code:
Constraints set by IB like this:
Here's the cells on the first launch:
After some scrolling:
Full code of the project can be found on Github.
According to this
Configure tableView as
func configureTableView() {
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
}
Call it on your viewDidLoad method
Than configure your uibutton height constraint to be greater then or equal.
Override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat where you can place your estimation height code
First off, it's better if you perform constraint updates in func updateConstraints() method of UIView. So instead of
override func layoutSubviews() {
super.layoutSubviews()
self.myButton?.layoutIfNeeded()
self.heightConstraint?.constant = self.myButton!.titleLabel!.frame.size.height
}
I would do
override func updateConstraints() {
self.myButton?.layoutIfNeeded()
self.heightConstraint?.constant = self.myButton!.titleLabel!.frame.size.height
super.updateConstraints()
}
Note that you should call the super implementation at the end, not at the start. Then you would call cell.setNeedsUpdateConstraints() to trigger a constraint update pass.
Also you should never directly manipulate the cell bounds the way you are doing in heightForRowAtIndePath: method, and even if you are completely sure that manipulating directly is what you want, you should manipulate cell.contentView's bounds, not the cell's bounds. If you are looking to adjust the cell height dynamically with respect to the dimensions of the content, you should use self sizing cells. If you need to support iOS 7, then this answer tells you how to achieve that behaviour with autolayout only (without touching the bounds etc).
To reiterate the answer, you should do:
func viewDidLoad() {
self.dummyCell = CustomCell.init()
// additional setup
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
self.dummyCell.myButton?.setTitle(self.data[indexPath.row], forState: UIControlState.Normal)
self.dummyCell.layoutIfNeeded() // or self.dummyCell.setNeedsUpdateConstraints() if and only if the button text is changing in the cell
return self.dummyCell.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
}
Please know that the answer I linked to outlines a strategy to get the cell height via autolayout, so only writing the code changes I proposed won't be enough unless you set your constraints in a way that makes this solution work. Please refer to that answer for more information.
Hope it helps!
First of all, remove the height constraint of button and bind it to top and bottom with cell.
Then, in your cell' height, calculate height of the text based on the width and font of button. This will make the cell's height dynamic and you wont need height constraint anymore.
Refer the link below to get the height of text:
Adjust UILabel height to text
Hope it helps. If you need help further or understanding anything, let me know.. :)

Resources