UITableView dynamic cell heights only correct after some scrolling
I don't know this is clearly documented or not, but adding [cell layoutIfNeeded]
before returning cell solves your problem.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"];
NSUInteger n1 = firstLabelWordCount[indexPath.row];
NSUInteger n2 = secondLabelWordCount[indexPath.row];
[cell setNumberOfWordsForFirstLabel:n1 secondLabel:n2];
[cell layoutIfNeeded]; // <- added
return cell;
}
Animation in UITableViewCell breaks after cell deallocation
Based on your code and comment...
Instead of expanding your cell, you're setting clipsToBounds = false
and expanding the cell's contents, allowing them to extend outside the bounds of the cell.
As you scroll a UITableView
, cells are - of course - added / removed as needed. But when they are re-added, the "z-order" changes (because, with "standard" table view usage, it doesn't matter).
So, after scrolling down and then back up, and then tapping the "Show more" button, that cell may be (and usually is) at a lower z-order ... and thus its expanded "out-of-bounds" content is hidden behind the cell(s) below it.
If you want to stick with that approach (as opposed to expanding the cell itself), you can try implementing scrollViewDidScroll
to make sure that cell is "on the top":
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// make sure the first row is visible (not scrolled off the top of the view)
let pth = IndexPath(row: 0, section: 0)
if let a = tableView.indexPathsForVisibleRows,
a.contains(pth),
let c = tableView.cellForRow(at: pth) {
// bring it to the front -- i.e. the top of the z-order
tableView.bringSubviewToFront(c)
}
}
reloadData() of UITableView with Dynamic cell heights causes jumpy scrolling
To prevent jumping you should save heights of cells when they loads and give exact value in tableView:estimatedHeightForRowAtIndexPath
:
Swift:
var cellHeights = [IndexPath: CGFloat]()
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath] ?? UITableView.automaticDimension
}
Objective C:
// declare cellHeightsDictionary
NSMutableDictionary *cellHeightsDictionary = @{}.mutableCopy;
// declare table dynamic row height and create correct constraints in cells
tableView.rowHeight = UITableViewAutomaticDimension;
// save height
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:indexPath];
}
// give exact height value
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
if (height) return height.doubleValue;
return UITableViewAutomaticDimension;
}
Scroll table view to bottom when using dynamic cell height
Eventually, I have found the answer. The reason why scrolling to the bottom does not work (and inserting/deleting rows are buggy as well, as I found out later), is because cell height is not properly estimated
. To get around this, try to return close estimations in estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
. However, if you can't do that (and in most cases you won't be able to) there is still a great solution: Store the heights of the cells in a dictionary, and return those for the estimatedHeight
when cells are reused (e.g. table view is scrolled). I have found this solution in another question, so please go and check that out for the actual code on how you would carry this out. (although probably, many of you can write this for themselves)
Original solution by @dosdos
EDIT: Maybe to fix just scrolling to the bottom this is unnecessary, however highly recommended in my opinion. If you don't use this for estimating your row height, you will encounter other problems such as buggy scrolling and worse performance at a large number of cells.
Also you need to add this to viewDidAppear
let lastItem = IndexPath(item: dataSource.count - 1, section: 0)
tableView.scrollToRow(at: lastItem, at: .bottom, animated: true)
Related Topics
Could Not Cast Value of Type 'Uitableviewcell' to '(Appname).(Customcellname)'
iOS Check If Application Has Access to Microphone
Open Installed Pwa from External Url
Apple Push Notification Limitation
How Detect Swipe Gesture Direction
Xcode Attach to Process Doesn't Display Nslog
Uitextview Linespacing Causes Different Cursor Height Inbetween Paragraph Lines
Cannot Assign to Property in Protocol - Swift Compiler Error
How to Install iOS 7.0 and iOS 8.0 Simulators in Xcode 6.1
How to Get the Front Camera in Swift
Path Extension and Mime Type of File in Swift
How to Remove Border from Segmented Control
What Is Difference Between Urlwithstring and Fileurlwithpath of Nsurl
What Do the "M" and "A" Icons in the Project Navigator of Xcode 4 Mean When I Create a New Project
How to Convert Uicolor Value to a Named Color String