Skip to content

Instantly share code, notes, and snippets.

@christianselig
Created November 30, 2020 01:16
Show Gist options
  • Save christianselig/0d1c14834adfdf916cf7fc5a5aeefd5c to your computer and use it in GitHub Desktop.
Save christianselig/0d1c14834adfdf916cf7fc5a5aeefd5c to your computer and use it in GitHub Desktop.
I want to add a new cell to the top of the table view without causing the current position to be pushed down. Note that I'm also not removing the cell I would like to add to the top, but rather just also adding it to the top.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView(frame: CGRect.zero, style: .plain)
var totalAdded = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
view.addConstraint(NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: tableView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: tableView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 0.0))
}
func numberOfSections(in tableView: UITableView) -> Int {
return UILocalizedIndexedCollation.current().sectionTitles.count + 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? totalAdded : 8
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
cell.textLabel?.text = "Oranges \(indexPath.row)"
cell.accessoryView = createStarAccessoryButton()
return cell
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return ["*"] + UILocalizedIndexedCollation.current().sectionTitles
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "Favorites" : UILocalizedIndexedCollation.current().sectionTitles[section - 1]
}
func createStarAccessoryButton() -> UIButton {
let button = UIButton(type: .system)
button.tintColor = .lightGray
button.setImage(UIImage(systemName: "star.fill")!, for: .normal)
button.sizeToFit()
button.addTarget(self, action: #selector(starButtonTapped(sender:)), for: .touchUpInside)
return button
}
@objc func starButtonTapped(sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: tableView)
guard let indexPath = tableView.indexPathForRow(at: buttonPosition) else { return }
let height = tableView.cellForRow(at: indexPath)!.bounds.height
let oldContentOffset = tableView.contentOffset.y
totalAdded += 1
tableView.reloadData()
// The ol' "dispatch ahead one run loop iteration" sorta works most of the time :/
DispatchQueue.main.async {
self.tableView.contentOffset.y = oldContentOffset + height
}
}
}
@christianselig
Copy link
Author

Solution per Nathan Lawrence, don't use reloadData just use the cell insert methods! Doh!

@objc func starButtonTapped(sender: UIButton) {
    let buttonPosition = sender.convert(CGPoint.zero, to: tableView)
    guard let indexPath = tableView.indexPathForRow(at: buttonPosition) else { return }
    let height = tableView.cellForRow(at: indexPath)!.bounds.height
    
    let oldContentOffset = tableView.contentOffset.y
    
    totalAdded += 1
    
    tableView.performBatchUpdates {
        self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .none)
    } completion: { (didComplete) in
        self.tableView.contentOffset.y = oldContentOffset + height
    }
}

@christianselig
Copy link
Author

Oh gosh I didn't realize how much I butchered the indentation in the main gist. I'm so sorry!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment