Summary
Resizing and reloading rows in a tableView where every cell has a dynamic height calculated by constraints causes a lot of other cells to recalculate unnecessarily. How should I solve this?
An example project can be found here.
Long version
I have a view that shows the details of a company and a variable amount of cells (or cards) with more detailed information. I implemented this in a UITableViewController with dynamically sized cells, using auto layout constraints (mostly helped along by this Ray Wenderlich tutorial).
The view has the following cells with variable heights:
Section 0
Row 0: Header image
Row 1: Company information
Row 2: Buttons
Section X > 0
Row 0: About Card
The 'About Card' may or may not have a header image and can be expanded to show the entire text.
Currently I'm having four problems with this:
The UITableViewController is pushed with animating: true. However, the tableView starts out behind the navigation bar and jumps down when it's finished animating. No clue why.
Update This doesn't appear to happen consistently. Joy.
When doing an action that should reload the buttons to indicate the state change, it reloads the button cell nicely. However, it also causes the company info cell to jump down and animate upwards.
Updating the cell happens with the following code:
func organizationRequestsUpdated() {
self.tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: UITableViewRowAnimation.None)
}
Tapping an About Card should expand it. I'm currently achieving this by setting label.numberOfLines = 5 at the cell initiation and using this for toggling the expand cards:
#IBAction func tapped(sender: UITapGestureRecognizer)
{
if let view = sender.view?.superview as? OrganizationDetailAboutCardCell {
tableView.beginUpdates()
view.toggleReadMore()
tableView.endUpdates()
}
}
func toggleReadMore() {
let expanded = aboutTextLabel.numberOfLines == 5
aboutTextLabel.numberOfLines = expanded ? 5 : 0
}
I'm pretty sure all of these problems have to do with the automatic sizing of the cells with the constraints. However, I'm currently at a loss on how to fix it. Has anyone done automatic sizing of cells with constraints and updated the cell heights without all the tableView doing weird jumps?
Popping back to the UITableViewController has most of the cells at the wrong heights. No clue why.
Things I've tried
Problem 2
For problem number 2, I've already tried updating reloading only the button cell;
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 2, inSection: 0)], withRowAnimation: UITableViewRowAnimation.None)
However, this counterintuitively caused all the cells in the section to recalculate incorrectly, ending up with a heap of too small cells. I've also tried this UITableViewRowAnimation.Fade however, this had the same result.
Have you specified an estimated row height? I found this to be the source of many of my layout issues when I did this.
Another possible problem is that you need to assign fonts to labels to make them layout properly. So in your cellForRowAtIndexPath methods, specify the font that should be used on all labels/textfields/textviews.
Here is an example project here: https://github.com/sgoodwin/SelfSizingCells.
Good luck!
Related
I know there are tons of questions about tableViewCell disappearing on scroll, but none of those fit into my situation.
I say disappear, I mean the whole cell disappears, not some of its subviews.
Here's my situation:
I have a tableView with custom cells, each one of them is very complex, some could even host up to three collectionViews in them. Most of these cells are only displayed once, so I cache them. I retrieve data from network, and use it to populate the cells. As sometimes the data may not be fully available, some sections of the cell needs to be hidden: the cell will set some of its collectionViews' height constraint to zero, and isHidden to true, then notify the tableView, who will reload the cells' heights using:
tableView.beginUpdate()
tableView.endUpdate()
I have noticed two behaviors:
1:
If none of the cell's portion is hidden, the cell works perfectly, except when I scroll(from bottom up) too fast, the cell disappears before going out of the bound of the tableView. When I scroll slow, this does not happen.
2:
If some part of the cell is hidden, the cell will disappear before going out of tableView EVEN WHEN I scroll very slowly.
Sorry I cannot post any code, but what I might have done wrong?
P.S.: In my tableView's row height delegate method, I have:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath.row] ?? 0.1
}
cellHeights is where I use a dictionary to store computed height of the corresponding cell. I am certain that these height values are correct, because when I scroll from top down, the cell looks all right. This only happens when I scroll from bottom up.
I did some experiment with the default height 0.1. When I changed it to 1000, the cells are disappearing randomly even more. 0.1 looks relatively stable, but the problems above still exist.
Thanks.
In my UITableView after certain change to model i am trying to load the specific section in where my cells are getting loaded according to the model. But whenever the change happens there comes error with animation, which is like bouncing to top or down. how do i resolve it? So far nothing worked. This is how i am updating my section after api response:
DispatchQueue.main.async {
self.tableView.beginUpdates()
self.tableView.reloadSections(indexSet, with: .none)
self.tableView.endUpdates()
}
Because i had images of different sizes my tableview could not adjust the height calculation while reloading the table. So i have tried to pre calculate the previous height and adjust it to the new one. While implementing the thing it came to my mind that i can update the table from my cell and calling layoutIfNeededwould do the trick. So eventually it worked.
I am facing a strange issue with UITableView if I enable the UITableViewAutomaticDimension property.
I am fetchign some data from the server and I have kept my UITableView cell set to accept dynamic content and increase the height as per the content so far what i have done is
Set the number of lines of the UILabel to zero so that it grows as per the content
Added the below code inside the viewDidLoad method
self.myTable.estimatedRowHeight = 200
self.myTable.rowHeight = UITableViewAutomaticDimension
Now after i fetch the data from the server i reload the table and go to the last cell of the table view but my problem is when i reload my entire table after the webservice call,
the table view animates and scrolls to the top of the screen and is not visible unless I scroll it down.
I searched and googled but i was not able to find any solution to this but if any of you have every came across this kind of situation please guide me out.
I have even added the below line of code before returning the cell in the cellForRowAtIndexPath but still am stuck with the same issue
cell.layoutSubviews()
here's the code which i have added to scroll to the bottom cell after the successful service call
self.myTable.scrollToRowAtIndexPath(NSIndexPath(forRow: self.myArray.count - 1, inSection: 1), atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
I have a UITableView that's divided into sections, some of which I'm trying to make collapsible - tap on a section's header, and the cells in the section will expand/collapse.
I'm doing this with tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic), inside a self.tableView.beginUpdates() and self.tableView.endUpdates() block.
When opening a section for the first time (i.e. adding rows to the section for the first time), the cells inside aren't laid out properly. Instead of multiline text and a cell whose height expands to fit the required content, I get a one-line cell that's the standard 44pts high whose text is truncated with an ellipsis.
The cells' heights are usually controlled by estimatedHeightForRowAtIndexPath returning UITableViewAutomaticDimension.
The cells seem to get re-drawn and show their correct layout when they are scrolled off screen, or when a section is closed and re-opened.
So is there a way for me to make the cell/section/table redraw itself before the new cells are animated into view?
I've unsuccessfully tried things like self.tableView.reloadData() and reloadRowsAtIndexPaths, but I may not have been using these correctly.
I think I've got the right code, but it's possibly in the wrong place in the "rendering sequence", for want of a better term.
Many thanks!
I was just having the same problem and was able to fix this by setting an estimatedRowHeight for the tableView. The default value is 0 so setting it to a positive value seems to do the trick:
tableView.estimatedRowHeight = 44.0;
self.mainTableView.beginUpdates()
self.mainTableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade)
self.mainTableView.reloadRowsAtIndexPaths(self.mainTableView.indexPathsForVisibleRows!, withRowAnimation: UITableViewRowAnimation.Fade)
self.mainTableView.endUpdates()
This worked perfect for me.
I am having a tableview in which I am displaying my data. As you can assume from the title, these cells are dynamic in their content, and so should their height be. Actually I got it working, but I have a strange issue with the row 0 and 7. These 2 rows take my entire screen, but when I scroll past them and go back again everything is fine. I am not sure but the 7. cell must be among the first cells that are being "dequed" when I scroll down. But as I said, this is just the first time they are being displayed, when I scroll again to one of them, everything is looking good (when filling the tableview with new data it is also fine).
I tried to reload the tableview twice when it is being populated for the first time, but without success. The presentation of the first cell I have fixed calling : self.tableView reloadAtIndexPaths ... when the table is being populated for the first time,
and it worked, it has the height it should have regarding its size. But I still have the issue with cell number 7 (the above fix does not work for this cell, I assume because it is not being displayed at that moment)
I am not using any fancy height calulation, just the iOS 8 feature:
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 80
Is there a method, fix with which i can force the tableview to draw the cells again ?
Thanks
Cell size is calculated from constrains. So I assume there is something wrong with those or with data during computation.
try this:
in tableview cell subclass use -prepareForReuse method to clean cell data as before reuse.