I am having troubles with an autosizing UITableViewCell which contains a UICollectionView. The view hierarchy is roughly like this
UITableViewCell
-- Content View
---- Custom View
------ UIStackView (vertical)
--------- UILabel
--------- UICollectionView (vertical flow layout)
When the UITableViewCell is created or reused, the collectionView receives a datasource which it then starts to process. I need all cells of the collectionView to show in the tableViewCell, so I observe changes to the collectionView's content size in the Custom View like this
self.collectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.old, context: nil)
Once the collectionView finished rendering its content, I read its contentSize and set the height constraint of the collection view accordingly. This works fine.
The problem now is that all values calculate correctly, but the cell size does not change in the tableView. There are no autolayout errors or warnings in the console, so I assume all constraints are correct. The label in the stackView is also autosizing and this works fine.
I can pass info about the new contentSize of the stackView all the way up to the TableViewController, but I don't know what to call where to force autolayout to recalculate the height of an individual cell.
If I call tableView.reloadData() at the controller level if the height constraint of the collectionView in a cell does not match its content size, it does work. But it causes a delay and is obviously too expensive.
If I pass the cell that changed to the tableView and ask it to reload just the cell, the tableView does not find the indexPath of the cell (probably because it is still off screen). So this approach does not work.
Thanks!
First of all thanks to everybody for their responses!
After two days of pulling out my hair, it looks like UITableViewCell layout not updating until cell is reused solved it for me.
The problem was the missing
cell.layoutIfNeeded()
at the end of
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
Here you can see what it looks like now. The setup is like in the original question.
Again thanks everybody for your pointers!
Even thought this is an already answered question. But I have found this easy method in this tutorial to resize the UITableViewCell height according to the UICollectionView height size.
Add a height constraint to the UICollectionView and create an outlet for it in the UITableViewCell and don't forget to disable UICollectionView Scrolling and set its flow to vertical.
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
In the UITableViewDelegate cellForRow method use these
cell.frame = tableView.bounds
cell.collectionView.reloadData()
cell.layoutIfNeeded()
cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
I can tell it is working like charm and no need for observes.
Link for full tutorial
Related
I am trying to add custom height constraint to collection view cell which is present in uitableview cell. The height of constraint is dependent on image which needs to be fetched from url(Using sdwebimage to fetch image in collectionviewcell). Trying to to update constraint in sizeforitem in collectionviewcell. The problem i am facing because of this is that initially fixed height is getting set as per storyboard constraint and as soon as i am scrolling the height constraint is getting updated which is creating problems.
Need to know following
1)Need to know a proper way to implement this thing
2)Where and how should i update constraint so that the tableviewcell as well as collectionviewcell will be set to exact height as of image
3)Exactly where should i download images from sdwebimage
https://i.stack.imgur.com/brWvy.png
You can try to set height for collection view cell in
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {}
I think this will be helpful to you.
This function will be called every time once user start to swipe Up/Down or Left/Right in the case of the Horizontal collection view.
I nearly finished making my tableview cell and all the contents in data model shows up in the UI so I think the connection is fine. The problem is that my prototype cell shows only part of what it's supposed to show. I have 3 labels in the cell, but it only shows one and a half. the text is stuck to the left(I wanted to put it in center), and the textlabel is cut off so it doesn't show in the UI.
It's supposed to look like this.
I tried
tableView.estimatedRowHeight = tableView.rowHeight
tableView.rowHeight = UITableView.automaticDimension
this code in the ViewController to make my cell to automatically set height to my labels, but it didn't work.
I also used vertical stack view and made constraints so it fills the cell fully, and it doesn't seem to make a change.
Please help!
You have to use implement the UITableViewDelegate protocol and the function
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat
where you can return the custom height for each cell
You can do this in the Storyboard - you can just drag a cell in the UITableView to be bigger. The UI of resizing the table view cell is the same as resizing any UI element in the Storyboard. The rest of them will be too, though. Hope this helps!
I am learning iOS development and Swift. I am trying to create a simple to-do app, with custom cells in a TableView. I have created the custom cell with a XIB file. In this, the cell looks like this:
However, when I run the app on the simulator, it looks like this:
Where am I going wrong and how can I Fix it?
You can fix the cell height to a default value by putting something like
self.tableView.rowHeight = 44.0
In your viewDidLoad, where tableView corresponds to the IBOutlet that is your UITableView in your xib file.
If you would like to do this dynamically e.g. by calculating the size of the cell's contents and then resizing the cell so it fits nice and snug, you should implement
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44 // The cell's height
}
Be sure to set your UITableView's delegate to self to use this last method.
Please see this tutorial for more info.
I have a horizontal UIScrollView which is added as a subview inside custom UITableViewCell. For some cases, I want to change the scrollview content offset which I achieve by doing this code
cell.horizontalScrollView.setContentOffset(CGPoint(x: 300, y: cell.horizontalScrollView.contentOffset.y), animated: true)
inside the UITableView's cellForRowAtIndexPath function and tableview.reloadData() is called whenever I needed to scroll it programmatically.
The issue is, this does not work for the first time. ie,
When the tableview is loaded for the first time, the scrollviews inside the visible cells does not get scrolled according to the code. Its offset is just at (0,0). ---- (This is the issue!)
But after that, when I scrolled the tableView, then the new cells(reused cells) gets updated and scrollviews position gets changed. (which I needed from the start itself!)
So, I want to change the UIScrollView's initial offset(position) programmatically. How can I achieve that?
Atlast, I found the solution after sitting for a couple of days!
Had to implement layoutIfNeeded for the UITableViewCell inside the cellForRowAtIndexPath method before setting the contentOffset for the UIScrollView.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.layoutIfNeeded()
cell.horizontalScrollView.setContentOffset(CGPoint(x: 300, y: cell.horizontalScrollView.contentOffset.y), animated: false)
return cell
}
The best way to implement this is to subclass your cell and then set the content offset in layoutSubviews method.
I am trying to insert a lot of views into UITableViewCell.
I add view and calculate required constants. And I can't resolve one problem. Inner labels can be bigger than one line and when UITableView only initialized it didn't displays correctly: some cropped labels, wrong height and etc.
But when I reload tableview or scroll to bottom elements and scroll back to incorrect one cell all views displays correctly.
EDITED
Example: I have UITableViewCell. Every cell contains number of attributes(separate views). Each attribute view has title(gray text) and attribute value(single view again). Each attribute value view may have different height. And I assume height doesn't calculates right at the first time.
When I build UITableViewCell firstly I build all of attribute views and after that I add each view to Cell and calculate constants.
I have solve problem.
When I got UITableViewCell it frame has width 325 but tableView frame width is 375. Because of this inner views were build incorrect way. Now I just set contentView of Cell to tableView frame.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let identifier = "ListCell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier) as? ListViewCell
cell!.contentView.frame = tableView.frame
// additional setup
return cell!
}