I write custom jabber client in iphone.
I use xmppframework as engine.
And I have UITableViewController with NSMutableArray for repesent contact list.
<This is a solution for the case if you want to do table updates and keep the selection:
NSIndexPath* pathToSelect = [self.tableView indexPathForSelectedRow];
if (pathToSelect && newRows) {
int row = pathToSelect.row;
for (NSIndexPath* path in newRows) {
if (path.section == pathToSelect.section && path.row <= pathToSelect.row) {
row++;
}
}
pathToSelect = [NSIndexPath indexPathForRow:row inSection:pathToSelect.section];
}
[self.tableView beginUpdates];
if (reloadRows) [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationFade];
if (newSections) [self.tableView insertSections:newSections withRowAnimation:UITableViewRowAnimationFade];
if (newRows) [self.tableView insertRowsAtIndexPaths:newRows withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
if (pathToSelect) {
[self.tableView selectRowAtIndexPath:pathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
}
A lot of these answers are basically using hacks to re-select the row that was previously highlighted by finding out what the previously selected row was from the table view which seems like a bad way to go about it, because usually when your tableview reloads, there is a change in the data that is populating the tableview.
So rather base you re-selection of the row based on your data
let data = ["some", "array", "of", "data"]
let selected = "of" // this should be populated from didSelectRowAt indexPath:
tableView.reloadData()
if let index = data.firstIndex(of: selected) {
tableView.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none) // the "of" row should now be highlighted, regardless if the data changed order
}
Edit:
After testing, it doesn't always work!,
the reason is the index may change !
so the correct way is
make new variable
var currentIndex : IndexPath?
and on didSelectRow
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentIndex = indexPath
}
then change the function to
func reloadTableView() {
let indexPaths = tableview.indexPathsForSelectedRows
if let currentIndex = self.currentIndex {
self.tableview.reloadRows(at: [currentIndex], with: .fade)
for path in indexPaths ?? [] {
tableview.selectRow(at: path, animated: false, scrollPosition: .none)
}}
}
==========
@marmor Answer worked
this is a Swift version of his code
Swift 4.2.3
func reloadTableView() {
let indexPaths = tableView.indexPathsForSelectedRows
tableView.reloadData()
for path in indexPaths ?? [] {
tableView.selectRow(at: path, animated: false, scrollPosition: .none)
}
}
The workaround is to use reloadSections: instead of reloadData. For some reason reloadData removes the current selection.
my two cents for UICollectionView: (credits to Basil..)
I do run in main thread as I reload from a background task:
final override func safeReloadData(){
DispatchQueue.main.async { [weak self] in
guard let self = self else {
return
}
let indexPaths = self.collectionView.indexPathsForSelectedItems
self.collectionView.reloadData()
for path in indexPaths ?? [] {
self.collectionView.selectItem(at: path, animated: false, scrollPosition: [])
}
}
}
Adding a delay didn't work for me (tested on iOS 8.4 and iOS 9). What did work was adding a call to -layoutIfNeeded
on the selected cell, after calling -selectRowAtIndexPath:animated:scrollPosition:
.
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[[self.tableView cellForRowAtIndexPath:selectedIndexPath] layoutIfNeeded];