Обновление UITableView без прокрутки
У меня есть _TableView
с элементами, и я хочу установить автоматическое обновление, и я не хочу, чтобы он прокручивался при обновлении, скажем, пользователь прокручивал 2 страницы вниз, а обновление - трижды → поэтому я хочу поместите обновленный контент в верхнюю часть таблицы, не перебирая прокрутку пользователя.
Предположим, что пользователь был в строке 18
и теперь _dataSource
обновляется, поэтому он выбирает, скажем, 4 элемента, поэтому я хочу, чтобы пользователь оставался на том элементе, который у него был.
Каким будет лучший подход к его достижению?
Ответы
Ответ 1
Я показываю, добавляется ли только одна строка. Вы можете расширить его до нескольких строк.
// dataArray is your data Source object
[dataArray insertObject:name atIndex:0];
CGPoint contentOffset = self.tableView.contentOffset;
contentOffset.y += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView reloadData];
[self.tableView setContentOffset:contentOffset];
Но для этого вам нужно определить метод - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
. Или же вы можете напрямую указать высоту строки tableview, если она постоянна.
Ответ 2
Для Swift 3 +:
Вам нужно сохранить текущее смещение UITableView
, затем перезагрузить, а затем установить смещение на UITableView
.
Я создал эту функцию для этой цели:
func reload(tableView: UITableView) {
let contentOffset = tableView.contentOffset
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.setContentOffset(contentOffset, animated: false)
}
Просто назовите его: reload(tableView: self.tableView)
Ответ 3
SWIFT 3
let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)
Это ошибка iOS8 при использовании UITableViewAutomatic Dimension. Нам нужно сохранить смещение содержимого таблицы, перезагрузить таблицу, принудительно создать макет и вернуть contenOffset обратно.
CGPoint contentOffset = self.tableView.contentOffset;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
[self.tableView setContentOffset:contentOffset];
Ответ 4
Я делаю так:
messages.insertContentsOf(incomingMsgs.reverse(), at: 0)
table.reloadData()
// This is for the first load, first 20 messages, scroll to bottom
if (messages.count <= 20) {
let indexToScroll = NSIndexPath(forRow: saferSelf.messages.count - 1, inSection: 0)
table.scrollToRowAtIndexPath(indexToScroll, atScrollPosition: .Top , animated: false)
}
// This is to reload older messages on top of tableview
else {
let indexToScroll = NSIndexPath(forRow: incomingMsgs.count, inSection: 0)
table.scrollToRowAtIndexPath(indexToScroll, atScrollPosition: .Top , animated: false)
// Remove the refreshControl.height + tableHeader.height from the offset so the content remain where it was before reload
let theRightOffset = CGPointMake(0, table.contentOffset.y - refreshControl.frame.height - table.headeView.frame.height)
table.setContentOffset(theRightOffset, animated: false)
}
... также, так как я использую динамическую высоту ячейки, чтобы избежать некоторой странности, оценка кэшируется:
var heightAtIndexPath = [NSIndexPath: CGFloat]()
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
heightAtIndexPath[indexPath] = cell.frame.height
}
Ответ 5
Просто установите estimatedRowHeight
на максимально возможное значение.
self.tableView.estimatedRowHeight = 1000
self.tableView.estimatedSectionFooterHeight = 100.0
self.tableView.estimatedSectionHeaderHeight = 500.0
Вот оно !!
Примечание: пожалуйста, не используйте значение FLT_MAX, DBL_MAX
. Может быть, это сломает ваше приложение.
Ответ 6
Этот код предотвратит ненужную анимацию и поддержит смещение содержимого прокрутки, это сработало для меня.
let lastScrollOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadData()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastScrollOffset, animated: false)
Ответ 7
Использовать расширение
создайте UITableViewExtensions.swift
и добавьте следующее:
extension UITableView {
func reloadDataWithoutScroll() {
let offset = contentOffset
reloadData()
layoutIfNeeded()
setContentOffset(offset, animated: false)
}
}
Ответ 8
Swift 4.2: простое решение
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 0
self.tableView.estimatedSectionHeaderHeight = 0
self.tableView.estimatedSectionFooterHeight = 0
}
//And then simply update(insert, reloadSections, delete etc) your tableView or reload
tableView.reloadData()
//or
UIView.performWithoutAnimation {
tableView.beginUpdates()
.....
tableView.endUpdates()
}
Ответ 9
попробуйте заменить
reloadData
с
tableView.reloadRows
(at: tableView!.indexPathsForVisibleRows!, with: .none
),
но вы должны заботиться о no cells
, если no cells
, этот метод должен вызвать сбой.
Ответ 10
Если вы хотите перезагрузить, вы должны
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
а также используйте UITableViewDelegate
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 'your maximum cell height'
}
и ваш tableView останется в предыдущей позиции прокрутки без прокрутки
Ответ 11
В iOS 12.x
, используя Xcode 10.2.1
, более простой вариант.
UIView.performWithoutAnimation {
let loc = tableView.contentOffset
tableView.reloadRows(at: [indexPath], with: .none)
tableView.contentOffset = loc
}
Это работает лучше, чем следующее; он трясет время от времени, когда ряд не виден полностью.
let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)