Skip to content

Instantly share code, notes, and snippets.

@tikipatel
Last active September 24, 2019 07:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tikipatel/b249f78200c801d28ddea15b5d8953fc to your computer and use it in GitHub Desktop.
Save tikipatel/b249f78200c801d28ddea15b5d8953fc to your computer and use it in GitHub Desktop.
An example of animated rearranging of a UITableView
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