Search code examples
ioscellexpandable

How to make Expandable with only Cells (NOT SECTION/ HEADER) with UITableView in Swift?


I researched all StackOverflow about Expandable Table View, but they have posted only a show header with the cells. In fact that iOS 15 has changed the new design interfaces of the UITableView as known .insetGrouped.

Basically Header in .insetGrouped really terrible design interfaces for Expandable. I am trying to get my idea to use only cell to do Expandable without section needed.

My Table View codes Here:

class HelpViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    struct Object {
        var seactionName: String!
        var seactionObjection: [String]!
        var footerName: String!
    }
   
    var objectArray = [Object]()
    
    
    var TableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        
        objectArray = [
            Object(seactionName: "Header", seactionObjection: ["Expandable Cell", "One Row", "Two Row"],
                   
                footerName: "Footer"),
        
            
            // copy and paste new cells
        ]
   
        title = "Help Center"
        
        view.backgroundColor = UIColor.quaternarySystemFill
    
        TableView.frame = view.bounds
       
        TableView = UITableView(frame: self.view.bounds, style: UITableView.Style.insetGrouped)
        TableView.showsVerticalScrollIndicator = true
        TableView.indicatorStyle = .default
        TableView.register(SliceCell.self, forCellReuseIdentifier: "Slice List")
        
        TableView.delegate = self
        TableView.dataSource = self
        
        view.addSubview(TableView)
    
    }


    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return objectArray[section].seactionObjection.count
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
       
        return objectArray.count
        
    }
    
        func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

            return objectArray[section].seactionName

        }



func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
    return objectArray[section].footerName
}
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "Slice List") as! SliceCell
        
        cell.backgroundColor = .secondarySystemGroupedBackground
            cell.textLabel?.text = objectArray[indexPath.section].seactionObjection[indexPath.row]
            cell.textLabel?.numberOfLines = 0
        
        return cell
    }

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int){
    view.tintColor = .clear
    let header = view as! UITableViewHeaderFooterView
    header.textLabel?.textColor = UIColor.label
}


}

Well, I could change this code

cell.accessoryType = .disclosureIndicator

to this

 let disclosureIndicatorTapped = UIImageView()
        disclosureIndicatorTapped.image = UIImage(systemName: "chevron.forward") // When tapped automatic to "Chevron.down"
        cell.accessoryView = disclosureIndicatorTapped

Any idea how to get Expandable Cells in UITableView with Swift, without Header or Footer needed?

Thanks!


Solution

  • You can add a Bool "expanded" property to your Object.

    In numberOfRowsInSection, if expanded is True, return the count of items in seactionObjection, else return 1.

    Then, in didSelectRowAt, if it's the first row in a section, toggle the expanded property of that section's Object, and reload the section.

    Here's a modified version of your controller class:

    class HelpViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
        
        struct Object {
            var seactionName: String!
            var expanded: Bool!
            var seactionObjection: [String]!
            var footerName: String!
        }
        
        var objectArray = [Object]()
        
        var TableView = UITableView()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            
            
            objectArray = [
                Object(seactionName: "Header 1",
                       expanded: false,
                       seactionObjection: ["Expandable Cell 1", "One Row 1", "Two Row 1"],
                       footerName: "Footer 1"),
                
                Object(seactionName: "Header 2",
                       expanded: false,
                       seactionObjection: ["Expandable Cell 2", "One Row 2", "Two Row 2"],
                       footerName: "Footer 2"),
                
                Object(seactionName: "Header 3",
                       expanded: false,
                       seactionObjection: ["Expandable Cell 3", "One Row 3", "Two Row 3"],
                       footerName: "Footer 3"),
                
    
                // copy and paste new cells
            ]
            
            title = "Help Center"
            
            view.backgroundColor = UIColor.quaternarySystemFill
            
            TableView.frame = view.bounds
            
            TableView = UITableView(frame: self.view.bounds, style: UITableView.Style.insetGrouped)
            TableView.showsVerticalScrollIndicator = true
            TableView.indicatorStyle = .default
            TableView.register(SliceCell.self, forCellReuseIdentifier: "Slice List")
            
            TableView.delegate = self
            TableView.dataSource = self
            
            view.addSubview(TableView)
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // if section is expanded
            //  return count of seactionObjection
            // else
            //  return 1
            return objectArray[section].expanded ? objectArray[section].seactionObjection.count : 1
        }
        
        func numberOfSections(in tableView: UITableView) -> Int {
            return objectArray.count
        }
        
        func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
            return objectArray[section].seactionName
        }
        
        func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
            return objectArray[section].footerName
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Slice List") as! SliceCell
            
            cell.backgroundColor = .secondarySystemGroupedBackground
            cell.textLabel?.text = objectArray[indexPath.section].seactionObjection[indexPath.row]
            cell.textLabel?.numberOfLines = 0
            
            // if it's the first row in a section,
            //  show accessoryView with
            //      chevron.up or chevron.down
            //  based on section Object expanded property
            if indexPath.row == 0 {
                let chevronName = objectArray[indexPath.section].expanded ? "chevron.up" : "chevron.down"
                let img = UIImage(systemName: chevronName)
                let disclosureView = UIImageView(image: img)
                cell.accessoryView = disclosureView
            } else {
                // not the first row in a section, so
                //  clear the accessoryView
                cell.accessoryView = nil
            }
        
            return cell
        }
        
        func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int){
            view.tintColor = .clear
            let header = view as! UITableViewHeaderFooterView
            header.textLabel?.textColor = UIColor.label
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            if indexPath.row == 0 {
                objectArray[indexPath.section].expanded.toggle()
                tableView.reloadSections([indexPath.section], with: .automatic)
            } else {
                // do something else on row selection
            }
        }
        
    }