Created
November 30, 2020 18:29
-
-
Save christianselig/063dbce4690ec227e009b6e6e286cae2 to your computer and use it in GitHub Desktop.
Trying to insert a table view cell at the top, without offsetting the current scroll position and causing a jump. Insert occurs when you tap one of the star accessory views, and seems due to the two different types of cells used in the table.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: "Cell") | |
tableView.register(SubtitleTableViewCell.self, forCellReuseIdentifier: "SubtitleCell") | |
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 + 2 | |
} | |
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
if section == 0 { | |
return 3 | |
} else if section == 1 { | |
return totalAdded | |
} else { | |
return 8 | |
} | |
} | |
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
if indexPath.section == 0 { | |
let cell = tableView.dequeueReusableCell(withIdentifier: "SubtitleCell", for: indexPath) | |
cell.textLabel?.text = "Grapes \(indexPath.row)" | |
cell.detailTextLabel?.text = "I love grapes!" | |
return cell | |
} else { | |
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) | |
cell.textLabel?.text = "Oranges \(indexPath.row)" | |
cell.accessoryView = createStarAccessoryButton() | |
return cell | |
} | |
} | |
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { | |
if indexPath.section == 0 { | |
return 60.0 | |
} else { | |
return 44.0 | |
} | |
} | |
func sectionIndexTitles(for tableView: UITableView) -> [String]? { | |
return ["!", "*"] + UILocalizedIndexedCollation.current().sectionTitles | |
} | |
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { | |
if section == 0 { | |
return "Cool Stuff" | |
} else if section == 1 { | |
return "Favorites" | |
} else { | |
return UILocalizedIndexedCollation.current().sectionTitles[section - 2] | |
} | |
} | |
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 | |
print("Height: \(height)") | |
let oldContentOffset = tableView.contentOffset.y | |
totalAdded += 1 | |
tableView.performBatchUpdates { | |
self.tableView.insertRows(at: [IndexPath(row: 0, section: 1)], with: .none) | |
} completion: { (didComplete) in | |
self.tableView.setNeedsLayout() | |
self.tableView.layoutIfNeeded() | |
self.tableView.contentOffset.y = oldContentOffset + height | |
} | |
} | |
} | |
class SubtitleTableViewCell: UITableViewCell { | |
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | |
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) | |
} | |
required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
} | |
} |
@christianselig This one won't jump and will maintain contentOffset which is an old trick I used to do and it is ridiculously ugly but might be doing the job for you.
Replace the whole performBatchUpdates
with this.
UIView.setAnimationsEnabled(false)
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: 0, section: 1)], with: .automatic)
tableView.endUpdates()
UIView.setAnimationsEnabled(true)
var contentOffset = self.tableView.contentOffset
contentOffset.y = oldContentOffset + height
tableView.setContentOffset(contentOffset, animated: false)
@TheCoordinator Ooo, thank you, I always appreciate more tools in the ol' belt!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Even weirder, it only occurs on the first insert, all the subsequent inserts don’t cause jumps!