I have used UITableView.refreshControl with large titles. I am trying to mimic the way the native Mail app works with pull to refresh. The refresh control, for me, moves and doesn't stay stuck at the top of the screen like the Mail app does. Here is a playground:
//: 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
How can I get the spinner to stick to the top, and have it behave like the Mail app does? Also the navigation title does this weird thing where it moves back slower than the table data and causes some overlap to happen... Any help is appreciated!
EDIT JUNE 7, 2020
I switched to using a diffable datasource which fixes the overlap animation, however, there is still this weird glitch right when the haptic feedback occurs and causes the spinner to shoot up to the top and back down, so my original question still remains - how do we get the spinner to stay pinned at the top like the mail app. Thanks for taking a look at this unique issue :)!!!

