我有一个UITableView
,它实现了一种“无限滚动”。
这是通过计算新数据的 IndexPath
然后传递给 tableView.insertRows(at: indexPaths, with: .none)
来完成的。
我的数据是从 50
页的 API 返回的。然而,我看到的是,当用户快速滚动到底部时,表格将插入行,然后跳到更上面的部分,基本上失去了它们的位置。
我的表是使用此代码段创建的
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
,因为我不喜欢任何插入行的动画。
在我的 View 模型中,我注入(inject)了一个符合 UITableViewDataSource、UITableViewDelegate
的 FeedTableViewProvider
并具有以下方法
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.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
}
现在,当插入单元格并以新的偏移量跳跃时,它们应该以正确的高度进行渲染,这意味着 View 基本上应该保持在原来的位置。
关于ios - UITableView 在插入新行时更改位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56712959/