Ответ 1
Недавно я работал с reloadData
- reloadData
не менял contentOffset
или прокручивал представление таблицы. Фактически он остается прежним, если смещение меньше, чем новый объем данных.
CGPoint offset = [_table contentOffset];
[_table reloadData];
[_table setContentOffset:offset animated:NO]; //unuseful
// __block UITableView *tableBlock = _table;
// [self performBlock:^(id sender) {
// [tableBlock setContentOffset:offset];
// } afterDelay:2];
Я знаю, не знаю ни одного метода делегата, который reloadData
после reloadData
. И использование afterDelay:2
которое является своего рода хаком, может быть слишком коротким или слишком длинным, так как я могу его реализовать?
Недавно я работал с reloadData
- reloadData
не менял contentOffset
или прокручивал представление таблицы. Фактически он остается прежним, если смещение меньше, чем новый объем данных.
У меня были проблемы с этим, потому что я возился с размером ячейки в моем методе cellForRowAtIndexPath. Я заметил, что информация о калибровке отключена после выполнения reloadData, поэтому я понял, что мне нужно заставить ее компоновку непосредственно перед настройкой смещения содержимого.
CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
Вызов reloadData
в таблицеView не изменяет смещение содержимого. Однако, если вы используете UITableViewAutomaticDimension
, который был введен в iOS 8, у вас может возникнуть проблема.
При использовании UITableViewAutomaticDimension
необходимо написать метод делегата tableView: estimatedHeightForRowAtIndexPath:
и вернуть UITableViewAutomaticDimension
вместе с tableView: heightForRowAtIndexPath:
, который также возвращает то же самое.
Для меня у меня были проблемы с iOS 8 при использовании этого. Это связано с тем, что метод estimatedHeightForRowAtIndexPath:
возвращал неточные значения, хотя я использовал UITableViewAutomaticDimension
. Это была проблема с iOS 8, поскольку никаких проблем с устройствами iOS 9 не было.
Я решил эту проблему, используя словарь, чтобы сохранить значение высоты ячейки и вернуть ее. Это то, что я сделал.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *key = @(indexPath.row);
NSNumber *height = @(cell.frame.size.height);
[self.cellHeightsDictionary setObject:height forKey:key];
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *key = @(indexPath.row);
NSNumber *height = [self.cellHeightsDictionary objectForKey:key];
if (height)
{
return height.doubleValue;
}
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
Проверка наличия высоты для загрузки первой страницы.
По умолчанию reloadData сохраняет contentOffset. Тем не менее, он может быть обновлен, если у вас есть неверные оценочные значения RowHeight.
fileprivate var heightDictionary: [Int : CGFloat] = [:]
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
heightDictionary[indexPath.row] = cell.frame.size.height
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let height = heightDictionary[indexPath.row]
return height ?? UITableViewAutomaticDimension
}
Этот метод следует вызывать вместо reloadData
. Это может соответствовать конкретным случаям.
public func reloadDataAndKeepOffset() {
// stop scrolling
setContentOffset(contentOffset, animated: false)
// calculate the offset and reloadData
let beforeContentSize = contentSize
reloadData()
layoutIfNeeded()
let afterContentSize = contentSize
// reset the contentOffset after data is updated
let newOffset = CGPoint(
x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
setContentOffset(newOffset, animated: false)
}
В моем случае снимите отметку высоты строки автоматически и оцените автоматическую задачу
Если вы вставляете данные в начало массива dataSource, вам нужно изменить contentOffset
следующим образом:
Swift 3 +
func prepareReloadData() {
let previousContentHeight = tableView.contentSize.height
let previousContentOffset = tableView.contentOffset.y
tableView.reloadData()
let currentContentOffset = tableView.contentSize.height - previousContentHeight + previousContentOffset
tableView.contentOffset = CGPoint(x: 0, y: currentContentOffset)
}
У меня была такая же проблема, однако ни один из предложенных здесь ответов не работал. Вот как я решил это. Подкласс UITableView
и переопределить метод layoutSubviews
следующим образом:
override func layoutSubviews() {
let offset = contentOffset
super.layoutSubviews()
contentOffset = offset
}
Если вы реализуете estimatedHeightForRowAtIndexPath
метод и ваша оценка не является правильным, вы возможно попасть в эту ситуацию.
Чтобы решить эту проблему, вы можете вернуть большую высоту, которая больше, чем каждая высота ячейки в вашем TableView, например:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 800.f; // if 800 is bigger than every possible value of your cell height.
}
Ответ @Skywalker показал наилучшее обходное решение для оценки высоты клеток. Но иногда проблема лишает в другом месте.
Иногда проблема лишена в contentInsets табличного представления. Если вы делаете данные перезагрузки, а таблицаView не видна на экране, вы можете столкнуться с неправильным смещением после появления на экране табличного представления.
Это происходит потому, что UIViewController может контролировать вставки, если его scrollView, когда scrollView появляется, позволяет лгать scrollView под прозрачной навигационной панелью и статусом.
Я столкнулся с таким поведением в iOS 9.1
Это сработало для меня (Swift 3)
self.table.reloadData()
self.table.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.top, animated: true)
Это работает 100%
change the tableView.reloadData()
в
tableView.reloadRows(at: tableView!.indexPathsForVisibleRows!, with: .none)
Для этого отлично работает
[tView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];