我已经使用带有大标题的UITableView.refreshControl。我正在尝试模仿本机Mail应用程序与pull刷新一起工作的方式。对我来说,刷新控件会移动,并且不会像Mail应用程序那样停留在屏幕顶部。这是一个游乐场:
//: 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
我如何使微调器固定在顶部,并使它的行为类似于Mail应用程序?另外,导航标题的作用很奇怪,它向后移动的速度比表数据慢,并导致发生一些重叠...感谢您提供任何帮助!
2020年6月7日编辑
我切换为使用可修复重叠动画的可扩散数据源,但是,当触觉反馈发生并导致微调器从顶部向上向下倾斜时,仍然存在这种奇怪的故障,因此我的原始问题仍然存在-我们如何使微调框像邮件应用程序一样固定在顶部。感谢您关注这个独特的问题:)!!!
我真的不知道如何回答第一个问题,但是既然您问了两个问题,我将继续努力,同时尝试第二个问题。
行到达顶部的速度过快的问题是由于使用了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)
}
}
以这种方式,将在行中添加动画,并且向下滚动动画不会被任何reloadData()
中断。
如果您没有仅要添加(或删除)的行,并且可能需要对两者进行某种组合,则可以使用performBatchUpdates方法在同一动画块中进行添加和删除。 (iOS 11 +)
当然,只有在您能够计算要添加和删除的项目的索引时,这才可行。请记住,添加和删除的行数应始终由数据结构中的实际更改反映出来,否则表视图会引发令人讨厌的异常。
无论如何,总应该是可行的,但是如果您要开箱即用,则可以使用iOS 13 diffable data source,或者,如果要在iOS 13之前使用,请使用instagram的IGListKit。
两者几乎都使用相同的概念来区分输入数组并自动检测删除和插入。因此,更改数组将自动反映在tableView行中的动画更改中。当然,它们都是实现tableView的非常不同的方式,因此您真的不能只更改代码的一部分来使其工作。
希望这会有所帮助。
原因是在使用中reloadData
,它会同时删除并重建所有内容,从而破坏了最终刷新的动画。
可能的解决方案是给动画结束时间,然后再执行重新加载数据。
使用Xcode 11.4 /游乐场测试
修改后的代码:
@objc func refreshIt(_ sender: Any) {
// simulate loading
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.refresh.endRefreshing() // << animating
// simulate data update
self.data = [6,7,8,9,10]
// forcefully synchronous, so delay a bit
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.tableView.reloadData()
}
}
}
替代方法,如果您知道修改后的数据结构,将使用beginUpdate/endUpdate
对
@objc func refreshIt(_ sender: Any) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.refresh.endRefreshing()
self.data = [6,7,8,9,10]
// more fluent, but requires knowledge of what is modified,
// in this demo it is simple - first section
self.tableView.beginUpdates()
self.tableView.reloadSections(IndexSet([0]), with: .automatic)
self.tableView.endUpdates()
}
}