Animate Cells As They Are Created Swift
Animate Cells As They Are Created Swift
To avoid confusion, this article focuses on expanding and contracing the height of a single tableview cell when it is tapped. I googled around and establish out many people used the same terms for inserting new cells nether the tapped cell, which is not what this article is most, I will write on this topic in the hereafter.
At the cease of this tutorial, y'all volition be able to implement this :
This tutorial assumes yous know how to implement tableview prison cell with dynamic peak (Automatic Dimension), as in each cell can contain different content height, eg: lengths of label text, different image sizes.
Say we have a table view cell with prototype and label fix like below :
You can download the starter project here : Expanding cell starter projection
The image view (avatarImageView) and characterization (messageLabel) are initialized in the cellForRowAt function :
// ViewController.swift extension VariableViewController : UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { render three } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // if cant dequeue prison cell, return a default cell guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? VariableTableViewCell else { print("failed to get cell") render UITableViewCell() } cell.avatarImageView.prototype = UIImage(named: avatars[indexPath.row]) jail cell.messageLabel.text = letters[indexPath.row] cell.messageLabel.numberOfLines = 0 return cell } }
Earlier nosotros implement the expanding / contracting animation, we would demand to declare an IndexSet to keep track of which prison cell accept been expanded, by storing the row index into the IndexSet, so that when we tap on the expanded cell, it would contract instead of expanding.
Employ IndexSet to proceed track of which cell has been expanded
From Apple documentation on IndexSet,
A collection of unique integer values that represent the indexes of elements in another collection.
You tin remember of IndexSet like an array, information technology can concord multiple integers, but non of the integers are repeating. This way, we won't need to worry nearly adding repeating row index into the array.
"Why tin can't we merely utilise an array of boolean to track which cell has been expanded?" , I heard you, this solution works too, say you lot tin can cheque if a cell has been expanded like this :
// 10 cells var expandedCells : [Boolean] = Array(repeating: false, count: 10) if(expandedCells[index] == true) { // ... show the cell expanded }
This works well if you have a fixed amount of cells in the tabular array view, but if yous take dynamic data that will be added from external API, say like Facebook timeline which keep adding more statuses as you curl, y'all will need to adapt the size of the expandedCells array as more than cells are existence added, this process can easily exist error prone!
By using IndexSet, nosotros won't need to worry virtually the size for the dataSource, we merely need to add or remove index of the jail cell that is expanded.
Nosotros can define an IndexSet to keep rail of the index of cell expanded in the view controller like this :
class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! // this will contain the index of the row (integer) that is expanded var expandedIndexSet : IndexSet = [] // ... }
Animative the cell to expand and contract
Now that we accept an IndexSet to continue track of which cell has been expanded, we can layout the cell expanded / contracted state in cellForRowAt like this :
extension VariableViewController : UITableViewDataSource { // ... func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "jail cell", for: indexPath) as? VariableTableViewCell else { impress("failed to get cell") return UITableViewCell() } prison cell.avatarImageView.prototype = UIImage(named: avatars[indexPath.row]) jail cell.messageLabel.text = letters[indexPath.row] // if the cell is expanded if expandedIndexSet.contains(indexPath.row) { // the label tin accept as many lines it demand to brandish all text cell.messageLabel.numberOfLines = 0 } else { // if the prison cell is contracted // only show first three lines jail cell.messageLabel.numberOfLines = three } render cell } }
Equally we want the cell to exist expanded / contracted on tap, we then implement the didSelectAtRow delegate function :
extension ViewController : UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, blithe: true) // if the cell is already expanded, remove it from the indexset to contract it if(expandedIndexSet.contains(indexPath.row)){ expandedIndexSet.remove(indexPath.row) } else { // if the cell is non expanded, add together it to the indexset to aggrandize it expandedIndexSet.insert(indexPath.row) } // the animation magic happens here // this will call cellForRow for the indexPath you supplied, and animate the changes tableView.reloadRows(at: [indexPath], with: .automatic) } }
The central for the animation is calling tableView.reloadRows(at:, with:) . You supply an array of rows (indexpath) that needs to exist reloaded, so the tableview volition reload them past calling cellForRowAt: on these cell to layout them again, and animate the transition with the RowAnimation you passed into the with: parameter.
Hither's a list of available RowAnimation we tin can use to breathing the cell row changes, but mostly we will utilise the .automatic or .fade event as it looks better.
Implementing the cell blitheness was easier than I originally thought! Thanks reloadRows for the heavy lifting.
If you want to cheque if an UILabel is truncated (with "..." at the end of text because of clipping), I accept written a short extension here, this might be useful if you are checking whether to show a "read more" button on the tableview cell depending if the label is beingness truncated. eg. If the the label text is short and didn't get truncated, don't prove the "read more" button.
Download the sample expanding cell projection
Not certain if you are doing it right on the expanding cell?
Download the sample project and compare it yourself!
https://github.com/fluffyes/expandingCell
Tired of fighting with Auto Layout constraints? Why is it so hard to make a layout to piece of work?! Would using lawmaking for UI and constraints make it easier? (No, non really) If yous want to understand Motorcar Layout fundamentally (instead of only following youtube tutorials implementing a very specific layout which might not apply to your app), check out my book Making Sense of Auto Layout, with applied instance study!
"It helped me understand how Auto Layout works fundamentally - something that I couldn't notice in whatsoever tutorial. I realized what Auto Layout needs for each view to render information technology correctly" – Ostik Lebyak
DOWNLOAD HERE
Animate Cells As They Are Created Swift
Posted by: arreolaraides.blogspot.com
0 Response to "Animate Cells As They Are Created Swift"
Post a Comment