Last active
September 24, 2019 07:53
-
-
Save tikipatel/b249f78200c801d28ddea15b5d8953fc to your computer and use it in GitHub Desktop.
An example of animated rearranging of a UITableView
This file contains 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 PlaygroundSupport | |
import UIKit | |
/** | |
This playground is an example of how how to move cells when the data source of a table view | |
changes. | |
*/ | |
class TVDataSource: NSObject, UITableViewDataSource { | |
private let tableView: UITableView | |
init(tableView: UITableView) { | |
self.tableView = tableView | |
super.init() | |
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") | |
tableView.dataSource = self | |
} | |
// The data to show in the table view | |
private var options = [1, 2, 3, 4, 5] | |
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
return options.count | |
} | |
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) | |
cell.textLabel?.text = "\(options[indexPath.row])" | |
return cell | |
} | |
@objc func handleRefresh() { | |
// New order of the data | |
let shuffledOptions = options.shuffled() | |
// The difference between the old data and the new data | |
let difference = shuffledOptions.difference(from: options) | |
// An array to hold the removed index paths | |
var removeIndexPaths: [IndexPath] = [] | |
// An array to hold the inserted index paths | |
var insertIndexPaths: [IndexPath] = [] | |
// Get all the differences and populate the arrays above | |
for change in difference { | |
switch change { | |
// If the change is a `remove`, then we want to remove that row | |
case .remove(let offset, _, _): | |
removeIndexPaths.append(IndexPath(row: offset, section: 0)) | |
// If the change is an `insert`, then we want to insert that row | |
case .insert(let offset, _, _): | |
insertIndexPaths.append(IndexPath(row: offset, section: 0)) | |
} | |
} | |
// Assign the new data before inserting / removing because | |
// tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell | |
// will be triggered and we want the new data to be available to the inserted cells | |
options = shuffledOptions | |
/* | |
https://developer.apple.com/documentation/uikit/uitableview/1614987-moverow | |
You can combine row-move operations with row-insertion and row-deletion operations within a | |
beginUpdates()–endUpdates() block to have all changes occur together as a single animation. | |
*/ | |
tableView.beginUpdates() | |
// Rows will be inserted from the left | |
tableView.insertRows(at: insertIndexPaths, with: .left) | |
// Rows will be deleted to the right | |
tableView.deleteRows(at: removeIndexPaths, with: .right) | |
tableView.endUpdates() | |
} | |
} | |
// Code to show the table view and refresh button | |
let view = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 400, height: 400))) | |
view.backgroundColor = .white | |
let tableView = UITableView() | |
let dataSource = TVDataSource(tableView: tableView) | |
view.addSubview(tableView) | |
tableView.translatesAutoresizingMaskIntoConstraints = false | |
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true | |
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true | |
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true | |
let refreshButton = UIButton() | |
refreshButton.setTitle("Refresh", for: .normal) | |
refreshButton.setTitleColor(.red, for: .normal) | |
view.addSubview(refreshButton) | |
refreshButton.translatesAutoresizingMaskIntoConstraints = false | |
refreshButton.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true | |
refreshButton.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true | |
refreshButton.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true | |
refreshButton.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 12).isActive = true | |
refreshButton.addTarget( | |
dataSource, | |
action: #selector(TVDataSource.handleRefresh), | |
for: .touchUpInside | |
) | |
PlaygroundPage.current.liveView = view |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment