如何在使用RxSwift将单元格添加/删除到UITableView后正确滚动

9avjhtql  于 2023-08-02  发布在  Swift
关注(0)|答案(1)|浏览(121)

我有一个带有tableView的视图控制器,其中有一个部分,我使用RxSwift以下面的方式在其中显示data。每当data更改时,我想滚动到tableView的底部(或其他任何位置)。我使用.xib文件作为我的单元格和动画数据源。
问题是,有时调用scrollToRow时,单元格尚未显示在tableView中,调用会抛出错误。

如何解决这个问题?

我唯一能想到的是在数据更改和滚动之间放置一个延迟,但是延迟应该有多大?我也认为有一些更强大的方法来解决这个问题。一直等到单元格真正显示出来。
我试着做:

  • scrollRectToVisible(...)tableView.contentSize的底部
  • 滚动前检查是否data.count == tableView.numberOfRows(inSection: 0)

这两种情况都会导致tableView在更新data时有时不滚动,这比错误好,但仍然不是预期的结果。

class SomeVC: UIViewController {
    private typealias DataSource = RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<..., ...>>

    private let db = DisposeBag()

    @IBOutlet weak var tableView: UITableView!

    private let data = BehaviorRelay<[...]>(value: ...)
    
    private lazy var dataSource = createDataSource()
    
    override func viewDidLoad() {
        
        tableView.register(UINib(nibName: ..., bundle: ...), forCellResuseIdentifier: ...)
        dataSource.animationConfiguration = AnimationConfiguration(insertAnimation: .bottom, reloadAnimation: .fade, deleteAnimation: .fade)

        data
            .asDriver()
            .do(afterNext: { [unowned self] data in
                DispatchQueue.main.async { [weak self] in
                    let lastIndexPath = getLastIndexPath(from: data)
                    // This does not always hold: data.count == tableView.numberOfRows(inSection: 0)
                    self?.tableView.scrollToRow(at: lastIndexPath, at: .none, animated: true)
                }
            }
            .map { [AnimatableSectionModel(...)] }
            .drive(tableView.rx.items(dataSource: dataSource))
            .disposed(by: db)
    }

    private func getLastIndexPath(from data: [...]) {
        // Something like this
        return IndexPath(row: data.count - 1, section: 0)
    }

    private func createDataSource() -> DataSource {
        return DataSource(configureCell: { dataSource, tv, indexPath, model in
            let cell = tv.dequeueReusableCell(withIdentifier: ..., for: indexPath)
            ...
            return cell
        }
    }
}

字符串

gojuced7

gojuced71#

下面的工作,但它不是理想的:

source
    .map { [SectionModel(model: "", items: $0)] }
    .bind(to: tableView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)

source
    .filter { $0.count > 0 }
    .map { IndexPath(row: $0.count - 1, section: 0) }
    .bind(onNext: { [tableView] indexPath in
        tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
    })
    .disposed(by: disposeBag)

字符串
重要的一点,也是最大的问题,这里是两个订阅依赖于顺序。由于它们不是独立的,如果你反转它们,应用程序将无法工作。
这也为我工作。也许你计算最后一行是错误的?

source
    .map { [SectionModel(model: "", items: $0)] }
    .do(afterNext: { [tableView] sections in
        if sections[0].items.count > 0 {
            tableView.scrollToRow(at: IndexPath(row: sections[0].items.count - 1, section: 0), at: .bottom, animated: true)
        }
    })
    .bind(to: tableView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)

相关问题