带有大标题导航的 UITableView UIRefreshControl 不会粘在顶部 - 它会移动

cri*_*ald 8 xcode uitableview ios uirefreshcontrol swift

我使用了带有大标题的 UITableView.refreshControl。我试图模仿本机邮件应用程序使用拉动刷新的方式。对我来说,刷新控件会移动并且不会像邮件应用程序那样停留在屏幕顶部。这是一个游乐场:

//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
    public init() {
        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    private lazy var refresh: UIRefreshControl = {
        let refreshControl = UIRefreshControl()
        refreshControl.backgroundColor = .clear
        refreshControl.tintColor = .black
        refreshControl.addTarget(self, action: #selector(refreshIt), for: .valueChanged)
        return refreshControl
    }()
    private lazy var tableView: UITableView = {
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.refreshControl = refresh
        tableView.delegate = self
        tableView.dataSource = self
        return tableView
    }()
    private var data: [Int] = [1,2,3,4,5]

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // Add tableview
        addTableView()

        navigationController?.navigationBar.prefersLargeTitles = true
        navigationItem.title = "View Controller"
    }
    private func addTableView() {
        view.addSubview(tableView)
        NSLayoutConstraint.activate([
            tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
            tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
    @objc func refreshIt(_ sender: Any) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.refresh.endRefreshing()
            self.data = [6,7,8,9,10]
            self.tableView.reloadData()
        }
    }
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.data.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = "Row \(data[indexPath.row])"
        return cell
    }
}
let vc = ViewController()

let nav = UINavigationController(rootViewController: vc)

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = nav

Run Code Online (Sandbox Code Playgroud)

我怎样才能让微调器粘在顶部,并让它像邮件应用程序一样运行?此外,导航标题会做这件奇怪的事情,它比表格数据向后移动的速度慢,并导致发生一些重叠......感谢任何帮助!

编辑 2020 年 6 月 7 日

我转而使用可修复重叠动画的 diffable 数据源,但是,当触觉反馈发生并导致微调器向上和向下射击时,仍然存在这种奇怪的故障,所以我原来的问题仍然存在 - 怎么做我们让微调器像邮件应用程序一样保持固定在顶部。感谢您查看这个独特的问题 :)!!!

Enr*_*oza 0

我真的不知道如何回答第一个问题,但既然你问了两个问题,我会继续尝试同时帮助你解决第二个问题。

行到达顶部太快的问题是由于使用了tableView.reloadData(). 这总是在没有动画的情况下完成的,所以发生的事情是可以预料的。

您可以做的是在表视图上使用其他方法,例如插入或删除行,如下所示:

@objc func refreshIt(_ sender: Any) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.refresh.endRefreshing()


            let newData = [6,7,8,9,10]
            let newIndexPaths = newData.enumerated().map{IndexPath(row: self.data.count+$0.offset, section: 0)}

            self.data.append(contentsOf: newData)
            self.tableView.insertRows(at: newIndexPaths, with: .automatic)
        }
    }
Run Code Online (Sandbox Code Playgroud)

这样,行将添加动画,并且向下滚动动画不会被任何reloadData().

如果您不仅要添加(或删除)行,并且可能必须对两者进行某种组合,那么您可以使用performBatchUpdates 方法同一动画块中执行添加和删除操作。(iOS 11+)

当然,只有当您能够计算要添加和删除的项目的索引时,这才可行。请记住,添加和删除的行数应始终反映在数据结构中的实际更改中,否则表视图将抛出令人讨厌的异常。

无论如何,这应该总是可行的,但如果你想要开箱即用,你可以使用 iOS 13 diffable 数据源,或者,如果你想要在 iOS 13 之前使用它,你可以使用 instagram 的IGListKit

两者几乎都使用相同的概念来比较输入数组并自动检测删除和插入。因此,更改数组将自动反映在 tableView 行中的动画更改中。当然,它们都是实现 tableView 的非常不同的方式,因此您实际上不能只更改一小段代码来使其工作。

希望这能有所帮助。