У меня есть UITableView, который реализует тип «бесконечной прокрутки».
Это делается путем вычисления IndexPath новых данных и последующего перехода к tableView.insertRows(at: indexPaths, with: .none).
Мои данные возвращаются из API на страницах 50. Однако я вижу, что когда пользователь очень быстро прокручивает вниз, таблица будет вставлять строки, а затем переходить к разделу выше, по существу теряя свое место.
Моя таблица создана с использованием этого фрагмента
private func addTableView() {
tableView = UITableView(frame: .zero)
tableView.showsVerticalScrollIndicator = false
tableView.showsHorizontalScrollIndicator = false
tableView.rowHeight = UITableView.automaticDimension
tableView.bounces = true
tableView.estimatedRowHeight = 200
tableView.separatorStyle = .none
tableView.isHidden = true
tableView.backgroundColor = .clear
tableView.tableFooterView = UIView()
addSubview(tableView)
tableView.position(top: topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.reuseID)
}
И перезагрузка запускается с помощью
func insertRows(_ indexPaths: [IndexPath]) {
UIView.performWithoutAnimation {
tableView.insertRows(at: indexPaths, with: .none)
self.tableView.isHidden = false
}
}
Я использую performWithoutAnimation, так как мне не понравилась ни одна из анимаций вставки строк.
В моей модели представления я ввожу FeedTableViewProvider в соответствии с UITableViewDataSource, UITableViewDelegate и имею следующие методы
protocol FeedTableViewProviderType: class {
var data: Feed? { get set }
var feed: [FeedItem] { get }
var insertRows: (([IndexPath]) -> Void)? { get set }
var didRequestMoreData: ((Int) -> Void)? { get set }
}
class FeedTableViewProvider: NSObject, FeedTableViewProviderType {
var insertRows: (([IndexPath]) -> Void)?
var didRequestMoreData: ((Int) -> Void)?
var data: Feed? {
didSet {
guard let data = data else { return }
self.addMoreRows(data.feed)
}
}
private(set) var feed = [FeedItem]() {
didSet {
isPaginating = false
}
}
private var isPaginating = false
private func addMoreRows(_ data: [FeedItem]) {
var indexPaths = [IndexPath]()
data.indices.forEach { indexPaths.append(IndexPath(row: feed.count + $0, section: 0)) }
feed.append(contentsOf: data.sorted(by: { $0.props.createdDate > $1.props.createdDate }))
insertRows?(indexPaths)
}
private func requestNextPage() {
guard let currentPage = data?.currentPage, let totalPages = data?.totalPages, currentPage < totalPages else { return }
didRequestMoreData?(currentPage + 1)
}
}
extension FeedTableViewProvider: TableViewProvider {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feed.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.reuseID, for: indexPath)
cell.textLabel?.text = "Cell # \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.item == feed.count - 1 && !isPaginating {
isPaginating = true
requestNextPage()
}
}
}





Вы можете использовать эту функцию при каждой вставке данных новой строки в Tableview, чтобы предотвратить прокрутку вниз.
func scrollToTop(){
DispatchQueue.main.async {
let indexPath = IndexPath(row: self.dataArray.count-1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
Спасибо за ваш вклад @AzizulHoq, но я стараюсь не звонить tableView.reloadData()
используйте этот код при добавлении новых данных в табличное представление.
UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
self.tableView.reloadSections(NSIndexSet(index: 0) as IndexSet, with: UITableViewRowAnimation.none)
self.tableView.endUpdates()
Я подозреваю, что причина этого на самом деле связана с использованием
tableView.rowHeight = UITableView.automaticDimension
....
tableView.estimatedRowHeight = 200
Положение изменяется по мере изменения смещения при вставке новых ячеек.
Я бы начал с того, что сохранил какой-то кеш, содержащий высоту вашей ячейки.
private var sizeCache: [IndexPath: CGFloat] = [IndexPath: CGFloat]()
Затем вы можете зафиксировать это, когда ячейка прокручивается за пределы экрана.
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
sizeCache[indexPath] = cell.frame.size.height
}
Теперь обязательно примените этот размер из кеша.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return sizeCache[indexPath] ?? UITableView.automaticDimension
}
Теперь, когда ячейки вставлены и прыгают с новым смещением, они должны отображать свою правильную высоту, а это означает, что вид должен оставаться на месте.
Я не хочу, чтобы он прокручивался вниз или вверх, когда добавляются новые строки, я хочу, чтобы он сохранял текущую позицию.