reloadRowsAtIndexPaths updates the contentOffset Of the table view - ios

I am trying to reload a particular row from the tableview but the contentOffset of the tableview is reset to (0, 0) after the reload. I tried to add [tableview beginUpdates] and [tableview endUpdates] around the reloadRowsAtIndexPaths but didn't change the behavior.
The question was asked over here Calling reloadRowsAtIndexPaths removes tableView contentOffset but it did not solve the problem. I am pretty sure that animation has nothing to do with this behavior. I am not sure how to maintain the content Offset of the tableview while still reloading the tableview row.
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

I'm guessing you've implemented tableView:estimatedHeightForRowAtIndexPath: and have it returning 0.
After reloadRowsAtIndexPaths:withRowAnimation:, the tableView determines its offset using the estimated heights given in tableView:estimatedHeightForRowAtIndexPath:. So unless the value you return there is accurate, implementing it will cause your tableView's offset to change after the reload. I had a similar problem and fixed it by removing my implementation of tableView:estimatedHeightForRowAtIndexPath:.

Updating the estimatedRowHeight and estimatedSectionHeaderHeight with more accurate values can also help alleviate this issue if you haven't implemented any of the estimated height delegate methods.
You can use the view inspector to find the heights of your cells and headers.

Related

iOS11 UITableview reloadRowsAtIndexPaths glitch

Can anyone help me understand or solve this UITableView glitch in iOS11. I have managed to reproduce my issue in a fairly simple test case.
https://github.com/trapper-/iOS11-tableview-glitch
When reloading cells near the bottom of a tableview the reloaded cell will start to scroll weirdly and other cells start to move and hop around.
It gets much worse when reloading multiple cells, but I didn't want to complicate this simple example.
Edit: This issue only occurs in iOS11, works fine in iOS10 & iOS12. So
you need to download the iOS11 simulator to test this in Xcode 10
This should help:
[UIView performWithoutAnimation:^{
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
If I use like this, its working.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
Try providing estimatedRowHeight to 0 to table view on viewDidLoad. It works for me.
Please try setting you're table view estimatedRowHeight (in the viewDidLoad method):
self.tableView.estimatedRowHeight = 100;
The tableView.rowHeight is set to UITableViewAutomaticDimension by default but you need to set the estimatedRowHeight
From apple - "To enable self-sizing table view cells, you must set the table view’s rowHeight property to UITableViewAutomaticDimension. You must also assign a value to the estimatedRowHeight property. As soon as both of these properties are set, the system uses Auto Layout to calculate the row’s actual height."
You can check out:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html

heightForRowAtIndexPath not called for every cell

I'm implementing a chat functionality in my app, and I'm rolling my own UIViewController to do so. I'm using a UITableView instead of a UICollectionView because it fits my needs better.
Some of my messages are text-based (which can be multi-line) and some of them are image-based. I also have some cells in place to show headers (timestamp information) and footers ("Sending...", "Delivered", etc.).
When I build my data source, I am calculating the height needed for each of these cells, and returning that data in my tableView:heightForRowAtIndexPath: method.
I am trying to take more control over this process because automatic sizing of cells using auto layout doesn't work all that well when I want the UITableView to spend most of its time at the bottom, rather than at the top.
So, I have the following needs:
When the view loads, it should start fully scrolled to the bottom
At that point, the UITableView's contentSize should be correctly calculated
Inserting new cells at the bottom of the UITableView should work well and also animate well.
The problem I'm finding is that only some of my cells have their height checked by heightForRowAtIndexPath:.
For instance, here's a particular case:
I have 100 messages + 32 header/footer cells (132 total cells)
In viewWillAppear: (or viewDidAppear:, it doesn't seem to matter), I call this:
int lastRow = (int)(self.dataSource.count - 1);
NSIndexPath *lastPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
[self.tableView scrollToRowAtIndexPath:lastPath
atScrollPosition:UITableViewScrollPositionBottom
animated:animated];
It doesn't change much whether the animated variable is YES or NO. YES seems to call a few more times, but both of them only really call for values at the top and bottom (beginning/end) of my data source. This means that my calculated content height (which is correct) is not in line with self.tableView.contentSize.height.
If, however, I manually scroll up through all of the cells, everything gets sorted out and the UITableView is finally aligned with my calculated height.
In addition to the initial view needing to scroll to the bottom, I also want to be able to add new messages to new cells at the bottom of the UITableView and then animate them into view. That really doesn't work well if I let the UITableView manage its content size and its own animations.
Based on Apple's documentation, I expect it to call heightForRowAtIndexPath: for every cell. I'm getting that from here:
There are performance implications to using tableView:heightForRowAtIndexPath: instead of the rowHeight property. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more). See also tableView:estimatedHeightForRowAtIndexPath:.
So, my basic question is how I can force UITableView to let me set all of these values. I'm using auto layout within the UITableViewCell instances themselves, but I want to have control of cell sizing, rather than letting UITableView do it. Any suggestions?
Just adding tableView.estimatedRowHeight = 0 will solve the problem.

Changing Constraints of UITableViewCell, not affecting TableViewCell height in TableView

I am changing the constraint for Cell, But the height of the Cell remains same till deque, after dequeuing it gives correct result.
Any idea how can we reload the cell if the cell constraints are manipulated?
Using Rxswift for Cell View Model, but TableView is in Objc.
Thanks
It is always nice to share your code.
How are you changing the height of your cell? Are you using the heightForRowAtIndexPath delegate.
For changes to appear, I use this code.
[self.tableView beginUpdates];
[self.tableView endUpdates];
If this does not work for you, please share your code and I would update my answer accordingly.

UITableView dynamic cell heights - reload height for single row

I have a UITableView with rows of dynamic heights, many of which contain UITextViews. When the user starts typing in one of the textviews, I have the cells grow to accommodate the size of the textview content using the begin/endUpdates method. Using this method allows the cells to resize without losing keyboard focus on the textview, an important aspect of my app.
However, when I call begin/endUpdates, it reloads the heights for every cell that I have, and I was wondering if there was any way to only recompute the height of a cell at a particular indexPath while the user is typing. I want to do this because my heights for the other cells are expensive to compute as they have dynamic content. I know I could write some height caching code, but I was wondering if there was any method to only recompute the height of a specific cell or set of cells in a UITableView without losing keyboard focus / reloading the data content of that row?
I am using ios8, but will take an ios7/8 solutions.
Create of array of NSIndexPath's. If you have just one indexPath, add just this to the array, and use this method:
[self.tableView reloadRowsAtIndexPaths:[NSArray *array] withRowAnimation:UITableViewRowAnimationAutomatic];
but i think this method, will also hide the keyboard, never tried.
Doesn't seem like this is possible to do for only a single row. I ended up building a caching system for row heights so that at least it is faster to return heights for all cells.

Dynamic Height UITableViewCell data wrong order

Is it appropriate to call [tableView beginUpdates] and [tableView endUpdates] in cellforRowAtIndexPath?
I am trying to dynamically expand cell heights based on the content within the cell. When I use [tableView beginUpates] and [tableView endUpdates] in cellForRowAtIndexPath, the output looks ok. However, once I start scrolling, data in the cells becomes ordered incorrectly and sometimes the bar separator between table cells disappears.
Should I not be using [tableView beginUpdates] and [tableView endUpdates] for this?
Thanks!
You've got to specify your dynamic cell heights in heightForRowAtIndexPath because the table view wants to know its content size up-front. Any changes you make to the cell's frame in cellForRowAtIndexPath will be overridden by the table view, so what you're attempting won't work.
If your heights are derived from the cell's content, consider using the prototype approach outlined here. Also, if you can use a 3rd party library, check out TLIndexPathTools. It does dynamic cell height for free if your custom cell class implements the TLDynamicSizeView prototype. Try running the Dynamic Height sample project.

Resources