Expandable tableView in iphone

Viewed 37410

enter image description here

I want to make this type of expandable/collapsible table view. there are categories and subcategories as in picture. for example "health and beauty" is a category and when i click this cell than its open subcategories as in picture below. So how can I make this type of table view? please suggest me.

15 Answers

it is so easy to create an expandable tableview here is an example how I did this,

data I m using for this one

struct ItemList {
var name: String
var items: [String]
var collapsed: Bool

init(name: String, items: [String], collapsed: Bool = false) {
   self.name = name
   self.items = items
   self.collapsed = collapsed
   }
}

var sections = [ItemList]()

var items: [ItemList] = [
 ItemList(name: "Mac", items: ["MacBook", "MacBook Air"]),
 ItemList(name: "iPad", items: ["iPad Pro", "iPad Air 2"]),
 ItemList(name: "iPhone", items: ["iPhone 7", "iPhone 6"])
]

now just add this piece of code and use accordingly

extension ViewController:UITableViewDelegate,UITableViewDataSource{

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 60
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerHeading = UILabel(frame: CGRect(x: 5, y: 10, width: self.view.frame.width, height: 40))
    let imageView = UIImageView(frame: CGRect(x: self.view.frame.width - 30, y: 20, width: 20, height: 20))
    if items[section].collapsed{
        imageView.image = UIImage(named: "collapsed")
    }else{
        imageView.image = UIImage(named: "expand")
    }
    let headerView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60))
    let tapGuesture = UITapGestureRecognizer(target: self, action: #selector(headerViewTapped))
    tapGuesture.numberOfTapsRequired = 1
    headerView.addGestureRecognizer(tapGuesture)
    headerView.backgroundColor = UIColor.red
    headerView.tag = section
    headerHeading.text = items[section].name
    headerHeading.textColor = .white
    headerView.addSubview(headerHeading)
    headerView.addSubview(imageView)
    return headerView
 }
func numberOfSections(in tableView: UITableView) -> Int {
    return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let itms = items[section]
    return !itms.collapsed ? 0 : itms.items.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
    cell.textLabel?.text = items[indexPath.section].items[indexPath.row]
    return cell
}
@objc func headerViewTapped(tapped:UITapGestureRecognizer){
    print(tapped.view?.tag)
    if items[tapped.view!.tag].collapsed == true{
        items[tapped.view!.tag].collapsed = false
    }else{
        items[tapped.view!.tag].collapsed = true
    }
    if let imView = tapped.view?.subviews[1] as? UIImageView{
        if imView.isKind(of: UIImageView.self){
            if items[tapped.view!.tag].collapsed{
                imView.image = UIImage(named: "collapsed")
            }else{
                imView.image = UIImage(named: "expand")
            }
        }
    }
    tableView.reloadData()
}

}

and the result is Bingo :)

enter image description here

I had the requirement of expanding a single cell to a fuller view and collapsing it back to a summarised view.

So what I did was to design my cell using UIStackView. And I kept the view I didn't want to show in the collapsed state hidden and then showing it when the cell was tapped.

The trick here is to show and hide the view within tableView.beginUpdates() and tableView.endUpdates() statements. In this way table view automatically adjusts the cell height and does it animatedly.

Here is how a basic cell would look in the IB: enter image description here

Cells Custom Class:

    class AccordionCell: UITableViewCell {
    @IBOutlet weak var stackView: UIStackView!
    
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var extendedDescriptionLabel: UILabel!

    var expanded: Bool = false {
        didSet {
            if let extended = self.extendedDescriptionLabel {
                extended.isHidden = !expanded
            }
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.expanded = false
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }    
}

UITableView Delegate Implementation:

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! AccordionCell
        cell.titleLabel.text = "Row: \(indexPath.row)"
        cell.expanded = indexPath.row == expanded
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) as? AccordionCell {
            tableView.beginUpdates()
            if expanded == indexPath.row {
                cell.expanded = false
                expanded = -1
            }
            else {
                cell.expanded = true
                expanded = indexPath.row
            }
            
            tableView.endUpdates()
            tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
        }
    }
    
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) as? AccordionCell {
            tableView.beginUpdates()
            cell.expanded = false
            tableView.endUpdates()
        }
    }
}

In order to keep track which cell is expanded, I introduced a variable saving indexpath of currently expanded cell so that the right cell is expanded when tableview is scrolled.

Related