iosswiftxcodeexpandablelistview

How to handle open all/close all in tableview cell with each cell has View More/View less options in iOS Swift


I am doing Swift project, there it has view more/view less option for each cell. I have did it with following code. And it was for for View more/View less options.

//ViewController Class

var expanded:[IndexPath] = []

//Custom protocol

    func viewMoreTapped(cell: tableviewcell) {
        
        let indexpath = EntriesTableView.indexPath(for: cell)
        let indexPath = IndexPath(item: indexpath!.row, section: indexpath!.section)
        if(expanded.contains(indexPath)) {
            expanded.removeAll { (checkPath) -> Bool in
                return checkPath == indexPath
            }
        } else {
            expanded.append(indexPath)
        }
        entriesTableView.beginUpdates()
        entriesTableView.reloadRows(at: [indexPath], with: .none)
        entriesTableView.endUpdates()
    }

@IBAction func expandAllBtnAction(_ sender: Any) {
    
    isExpandedAll = !isExpandedAll
    expandAllButton.titlelabel.text = isExpandedAll ? "Open All" : "Close All"
    self.entriesTableView.reloadData()
}


        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                 let cell = tableView.dequeueReusableCell(withIdentifier:"cellIdentifier", for:
         indexPath) as! MyTableViewCell
        let checkPath = IndexPath(row:indexPath.row, section: indexPath.section)
        if expanded.contains(checkPath) {
            cell.cellConfigure(index: indexPath.row, isexpanded: true, info: Info, expandedAll: isExpandedAll)
        } else {
            cell.cellConfigure(index: indexPath.row, isexpanded: false, info: Info, expandedAll: isExpandedAll)
        }

    return cell

}

//Tableview cell class

    var isExpanded: Bool = false

    func cellConfigure(index: Int, isexpanded : Bool, info: Info, expandedAll: Bool) {

//For open all/close all handling
      if expandedAll && isExpanded == false {
            isExpanded = true
            viewMoreBtn.titleLabel?.text = "View Less"
        } else  {
            viewMoreBtn.titleLabel?.text = isExpanded ? "View Less" : "View More"
        }
  //view more/view less handling               cellHeight.constant = isExpanded ? 40 : 120

}

    @IBAction func viewMoreBtnAction(_ sender: Any) {
        
        isExpanded = !isExpanded
        delegate?.viewMoreTapped(cell: self) // this is custom protocol
    }

It is working fine for each cell View more/View less, But after user taps on open all, it should be open all cells and each cell if user has taps on view less, it should be close selected cell only. But, it's not working that time.

The requirement is

  1. if user taps on view more, it should be open selected cell. If user taps on view less, it should be close selected cell.
  2. Also if user taps on open all, All cells should be expanded, and also same time if user tap on view less for particular cell, that time only selected cell should be close.
  3. If user taps on close all, all cells should be close and if user taps on view less while close all showing, only selected cell should be close.

Any suggestions?

enter image description here


Solution

  • I would use an IndexSet to track the expanded rows; It is easier to insert, remove and check for the presence of index paths using a set.

    To collapse all of the rows you can simply remove all of the elements from the set. To expand all you insert all index paths into the set.

    You haven't provided detail on how your table data is stored. For the purposes of my answer I am going to use an array of arrays called data - The outer array is the sections and each inner array are the rows in that section. ie numberOfSections is data.count and the number of rows in a section is data[indexPath.section].count

    View Controller

    
    var expanded = [IndexSet]()
    
    //Custom protocol
    
    
    func loadData() {
        // After data is fetched
        self.expanded = Array(repeating: IndexSet(),count:data.count)
        self.tableView.reloadData()
    }
    
    func viewMoreTapped(cell: tableviewcell) {
            
       guard let indexPath = entriesTableView.indexPath(for: cell) else {
           return
       }
    
       let row = indexPath.row
       let section = indexPath.section
    
       if self.expanded[section].contains(row) {
           self.expanded[section].remove(row) 
       } else {
           self.expanded[section].insert(row)
       }
    
       self.entriesTableView.beginUpdates()
       self.entriesTableView.reloadRows(at: [indexPath], with: .none)
       self.entriesTableView.endUpdates()
    }
    
    @IBAction func expandAllBtnAction(_ sender: Any) {
        
        isExpandedAll.toggle()
        expandAllButton.titlelabel.text = isExpandedAll ? "Open All" : "Close All"
        
        if isExpandedAll {
            self.expanded = data.map { return IndexSet(integersIn: 0..<$0.count)}
        } else {
            self.expanded = Array(repeating: IndexSet(),count:data.count)
        }
        self.entriesTableView.reloadData()
    }
          
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier:"cellIdentifier", for:
             indexPath) as! MyTableViewCell
        cell.cellConfigure(isExpanded: self.expanded[indexPath.section].contains(indexPath.row), info: info) // A cell should never need to know its index path
    
        return cell
    }
    

    Cell

    var isExpanded: Bool = false
    
    func cellConfigure(isExpanded : Bool, info: Info) {
    
        viewMoreBtn.titleLabel?.text = isExpanded ? "View less":"View more"
        cellHeight.constant = isExpanded ? 40 : 120
    
    }
    
    @IBAction func viewMoreBtnAction(_ sender: Any) {
        delegate?.viewMoreTapped(cell: self) // this is custom protocol
    }
    

    A general note, variables and functions should start with a lower case letter. Class and struct names should start with an upper case letter.