I am trying to scroll to the bottom of a UITableView after it is done performing [self.tableView reloadData]
I originally had
[self.tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
But then I read that reloadData is asynchronous, so the scrolling doesn't happen since the self.tableView
, [self.tableView numberOfSections]
and [self.tableView numberOfRowsinSection
are all 0.
Thanks!
What's weird is that I am using:
[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
In the console it returns Sections = 1, Row = -1;
When I do the exact same NSLogs in cellForRowAtIndexPath
I get Sections = 1 and Row = 8; (8 is right)
And a
UICollectionView
version, based on kolaworld's answer:https://stackoverflow.com/a/43162226/1452758
Needs testing. Works so far on iOS 9.2, Xcode 9.2 beta 2, with scrolling a collectionView to an index, as a closure.
Usage:
You can use it for do something after reload data:
Swift:
Objective-C:
The reload happens during the next layout pass, which normally happens when you return control to the run loop (after, say, your button action or whatever returns).
So one way to run something after the table view reloads is simply to force the table view to perform layout immediately:
Another way is to schedule your after-layout code to run later using
dispatch_async
:UPDATE
Upon further investigation, I find that the table view sends
tableView:numberOfSections:
andtableView:numberOfRowsInSection:
to its data source before returning fromreloadData
. If the delegate implementstableView:heightForRowAtIndexPath:
, the table view also sends that (for each row) before returning fromreloadData
.However, the table view does not send
tableView:cellForRowAtIndexPath:
ortableView:headerViewForSection
until the layout phase, which happens by default when you return control to the run loop.I also find that in a tiny test program, the code in your question properly scrolls to the bottom of the table view, without me doing anything special (like sending
layoutIfNeeded
or usingdispatch_async
).I ended up using a variation of Shawn's solution:
Create a custom UITableView class with a delegate:
Then in my code, I use
Also make sure you set your table view to CustomTableView in the interface builder:
I had the same issues as Tyler Sheaffer.
I implemented his solution in Swift and it solved my problems.
Swift 3.0:
Swift 2:
Example Usage: