Alvin George
Alvin George

Reputation: 14294

How to make an expandable and collapsable UITableView (parent and child cells) in swift?

I wanted to have a UITableView which is able to expand and collapse while clicking on cells.

enter image description here

When I load the page, I want the Tableview to be expanded like this and function collapse and expand by clicking on the header(see the date).

Any help is appreciated.

Upvotes: 3

Views: 7439

Answers (3)

Nischal Hada
Nischal Hada

Reputation: 3288

**Try to implement below**

import UIKit


class BankDepositsHistoryVC: UIViewController {


    @IBOutlet weak var tableView: UITableView!

    let NORMAL_HEIGHT:CGFloat = 90
    let EXPANDABLE_HEIGHT:CGFloat = 200

    var SECTION_OTHER_CARDS = 0
    var expandableRow:Int = Int()


    override func viewDidLoad() {
        super.viewDidLoad()

        self.expandableRow =  self.historyData.count + 1 // initially there is no expanded cell
        self.tableView.reloadData()


    }




    // MARK: - TableViewDelegate Setup

    extension BankDepositsHistoryVC : UITableViewDelegate,UITableViewDataSource {

        func numberOfSectionsInTableView(tableView: UITableView) -> Int{
            return 1

        }

        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return self.historyData.count
        }




        func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{

            let value = indexPath.section
            let row = indexPath.row


            switch (value){

            case SECTION_OTHER_CARDS:

                switch (row){

                case self.expandableRow:
                    return EXPANDABLE_HEIGHT

                default:
                    return NORMAL_HEIGHT

                }

            default:
                return 0
            }

        }



        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

            let cell = tableView.dequeueReusableCellWithIdentifier("DepositsHistoryCell", forIndexPath: indexPath) as! DepositsHistoryCell
            cell.selectionStyle = UITableViewCellSelectionStyle.None
            return cell
        }


        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){

            self.expandableRow =  indexPath.row // provide row to be expanded
            //self.tableView.reloadSections(NSIndexSet(index: indexPath.row), withRowAnimation: UITableViewRowAnimation.Fade)
            self.tableView.reloadData()

        }

    }

Upvotes: 0

Alvin George
Alvin George

Reputation: 14294

Thanks everyone. I have solved the problem at last.This is the final sample code.

1) //Arrays for header and Child cells.

var topItems = [String]()
var subItems = [String]()

//Section index reference

var selectedIndexPathSection:Int = -1

2) Added Two custom cells. - one as header and other as Cell.

//  AgendaListHeaderTableViewCell.swift

import UIKit

class AgendaListHeaderTableViewCell: UITableViewCell {

    @IBOutlet weak var agendaDateLabel: UILabel!
    @IBOutlet weak var expandCollapseImageView: UIImageView!
    @IBOutlet weak var headerCellButton: UIButton!    

    override func awakeFromNib() {
        super.awakeFromNib()

    }

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

    }    
}

Child cell:

//  AgendaListTableViewCell.swift

import UIKit

class AgendaListTableViewCell: UITableViewCell {

    @IBOutlet weak var agendaListContainerView: UIView!
    @IBOutlet weak var moduleListTitleLabel: UILabel!
    @IBOutlet weak var moduleDueOnStatusLabel: UILabel!
    @IBOutlet weak var moduleLocationLabel: UILabel!
    @IBOutlet weak var moduleStatusLabel: UILabel!
    @IBOutlet weak var moduleDownLoadStatusImageView: UIImageView!
    @IBOutlet weak var moduleStatusLeftSideLabel: UILabel!


    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        agendaListContainerView.layer.cornerRadius =  3.0

        moduleStatusLabel.layer.borderWidth = 0.5
        moduleStatusLabel.layer.borderColor = UIColor.clearColor().CGColor
        moduleStatusLabel.clipsToBounds = true
        moduleStatusLabel.layer.cornerRadius =  5.0

    }

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

    }
}

3) On View Controller:

//  AgendaListViewController.swift

import UIKit

class AgendaListViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {

    @IBOutlet weak var agendaListTableView: UITableView!

    var appUIColor:UIColor = UIColor.brownColor()
    var topItems = [String]()
    var subItems = [String]()
    var selectedIndexPathSection:Int = -1

    override func viewDidLoad() {
        super.viewDidLoad()

        topItems = ["26th April 2017","27th April 2017","28th April 2017","29th April 2017","30th April 2017"]
        subItems = ["Monday","TuesDay","WednessDay"]

    }
    override func viewWillAppear(animated: Bool) {
        self.title = "AGENDA VIEW"
        self.automaticallyAdjustsScrollViewInsets =  false
        agendaListTableView.tableFooterView = UIView(frame: CGRectZero)
    }
//tableview delegate methods    
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

        return 85;

    }
    func numberOfSectionsInTableView(tableView: UITableView) -> Int
    {
        return topItems.count
    }
    func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {

        return 35
    }
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

        let headerCell = tableView.dequeueReusableCellWithIdentifier("agendaTableViewHeaderCellD") as! AgendaListHeaderTableViewCell
        headerCell.agendaDateLabel.text = topItems[section]as String

        //a buttton is added on the top of all UI elements on the cell and its tag is being set as header's section.

        headerCell.headerCellButton.tag =  section+100
        headerCell.headerCellButton.addTarget(self, action: "headerCellButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)

        //minimize and maximize image with animation.
        if(selectedIndexPathSection == (headerCell.headerCellButton.tag-100))
        {
            UIView.animateWithDuration(0.3, delay: 1.0, usingSpringWithDamping: 5.0, initialSpringVelocity: 5.0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
                headerCell.expandCollapseImageView.image =  UIImage(named: "maximize")
                }, completion: nil)
        }
        else{

            UIView.animateWithDuration(0.3, delay: 1.0, usingSpringWithDamping: 5.0, initialSpringVelocity: 5.0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
                headerCell.expandCollapseImageView.image =  UIImage(named: "minimize")
                }, completion: nil)
        }

        return headerCell
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if( selectedIndexPathSection == section){
            return 0
        }
        else {
            return self.subItems.count
        }
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let childCell = tableView.dequeueReusableCellWithIdentifier("agendaTableViewCellID", forIndexPath: indexPath) as! AgendaListTableViewCell
        childCell.moduleListTitleLabel.text = subItems[indexPath.row] as? String
        childCell.moduleLocationLabel.text = subItems[indexPath.row] as? String
        childCell.moduleDueOnStatusLabel.text = subItems[indexPath.row] as? String

        return childCell
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {


    }
    //button tapped on header cell
    func headerCellButtonTapped(sender:UIButton)
    {
        if(selectedIndexPathSection == (sender.tag-100))
        {
            selectedIndexPathSection = -1
        }
        else   {
            print("button tag : \(sender.tag)")
            selectedIndexPathSection = sender.tag - 100
        }

        //reload tablview
        UIView.animateWithDuration(0.3, delay: 1.0, options: UIViewAnimationOptions.TransitionCrossDissolve , animations: {
            self.agendaListTableView.reloadData()
            }, completion: nil)

    }   
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }    
}

The sample can be downloaded @ https://github.com/alvinreuben/Expand-ColllapseTableView

Upvotes: 6

Surjeet Rajput
Surjeet Rajput

Reputation: 1301

with the help of below method you can do that

for objective-c

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

for swift

func tableView(_ tableView: UITableView,heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat

just change the height for a row when its get clicked and reload the section.

when you design any cell in storyboard don't put bottom constraint on the expandable part.just put top and height constraint.Hope this help :)

Upvotes: -1

Related Questions