ios - UITableView 在插入新行时更改位置

标签 ios swift uitableview

我有一个UITableView,它实现了一种“无限滚动”。

这是通过计算新数据的 IndexPath 然后传递给 tableView.insertRows(at: indexPaths, with: .none) 来完成的。

我的数据是从 50 页的 API 返回的。然而,我看到的是,当用户快速滚动到底部时,表格将插入行,然后跳到更上面的部分,基本上失去了它们的位置。

enter image description here

我的表是使用此代码段创建的

  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、UITableViewDelegateFeedTableViewProvider 并具有以下方法

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/

相关文章:

iOS - 与多个应用程序目标共享 Today Extension?

ios - 无法验证客户端 3000

swift - 如何对 NSUserDefault 中的 NSMutableDictionary 存储数组进行排序?

ios - Swift 中的 NSNotificationCenter addObserver

iOS 在 UITableViewCell 中均匀间隔 5 个标签

ios - 如何通过 iOS 8 中提供的新 API 在 iPhone 6 上使用气压计?

ios - swift :Issue with increment Int inside String

iOS VoIP 推送 - 注册失败

Swift 3,交换两个表格单元格

iphone - 在不重复使用标识符的情况下使用多个自定义单元格