UITableView with Collapsible (expand and collapse) Cells
Table view cells should expand and shrink when clicked is a common use case.
Let's create a TableViewController with custom cells. Cells can be configured in three views:
- Cell View: Displays the contents of the cell in the default mode.
- Expanded View (Detailed View): Displays the contents of the cell in expanded mode.
- A container view (UIStackView) containing cells anddetailed views.
So CustomCell looks like this:
import UIKit
final class CustomTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private let containerView = UIStackView()
private let cellView = CustomTableCellView()
private let detailView = CustomTableDetailView()
func setUI(with index: Int) {
cellView.setUI(with: Constants.fruits[index])
detailView.setUI(with: index, image: UIImage(named: Constants.fruits[index]) ?? UIImage())
}
func commonInit() {
selectionStyle = .none
detailView.isHidden = true
containerView.axis = .vertical
contentView.addSubview(containerView)
containerView.addArrangedSubview(cellView)
containerView.addArrangedSubview(detailView)
containerView.translatesAutoresizingMaskIntoConstraints = false
cellView.translatesAutoresizingMaskIntoConstraints = false
detailView.translatesAutoresizingMaskIntoConstraints = false
containerView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
}
}
Now let's register this unit in the controller and add the required methods:
import UIKit
final class HomeTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(CustomTableViewCell.self,
forCellReuseIdentifier:"CustomTableViewCell")
guard let cell = tableView.dequeueReusableCell(
withIdentifier: "CustomTableViewCell") as? CustomTableViewCell
else { return UITableViewCell() }
cell.setUI(with: indexPath.row)
return cell
}
}
Now that our table view and custom cell with two views (folded view and expanded view) are ready, now we need to add settings to show and hide the detail view when the cell is touched.
extension CustomTableViewCell {
var isDetailViewHidden: Bool{
return detailView.isHidden
}
func showDetailView(){
detailView.isHidden = false
}
func hideDetailView(){
detailView.isHidden = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if isDetailViewHidden , selected {
showDetailView()
}else {
hideDetailView()
}
}
}
We use the setSelected method of tableViewCell here, so when the cell is selected, it will display detailView, when it is not selected, it will be hidden.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIView.animate(withDuration: 0.3){
self.tableView.performBatchUpdates(nil)
}
}
Now in order to make the expansion and collapse animation go smoothly, UITableView is helping to use a great API called:
performBatchUpdates(_:completion:)This API is available since iOS 11+. This method animates a collection of insert, delete, reload, and move actions. This can happen without charging the battery. The same effect can be achieved using `beginUpdates()` and `endUpdates()`, but Apple recommends that we should use `performBatchUpdates` instead. We can now call this method with an animation block to get a smoother selection of cells The effect, like
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = self.tableView.cellForRow(at: indexPath) as? CustomTableViewCell {
cell.hideDetailView()
}
}
, if we need to hide the detailView when selecting another cell, we can use to easily achieve.