Expanding the expandable table View cells to further extent

江枫思渺然 提交于 2019-12-10 11:52:43

问题


I created the Expandable table view cells which is like Below mentioned image. Library used is JExpandableTableView.

Code for Creating This ExpandableTable View is given below :

Model For Sections:

class SectionInfo: NSObject {

    var cells = [CellInfo]()
    var CategoryName: String
    var CategoryCount: String
    var CategoryImage: UIImage

    init(_ text: String,SubCount: String, Image: UIImage ) {

        self.CategoryName = text
        self.CategoryCount = SubCount
        self.CategoryImage = Image
    }
}

Model For SubCategoryCell:

class CellInfo: NSObject {

    var SubcategoryName: String!
    var SubcategoryCount: String!


    init(_ SubName: String, SubCount: String) {

        self.SubcategoryName = SubName
        self.SubcategoryCount = SubCount
    }
}

View Controller :

class CategoryVC: BaseVC,JExpandableTableViewDataSource,JExpandableTableViewDelegate{

    @IBOutlet weak var tblViewCategory: JExpandableTableView!

    var tableViewData = [SectionInfo]()
    var expandedIndexPath: IndexPath?

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Category"
        self.tblViewCategory.dataSource = self
        self.tblViewCategory.delegate = self
        self.tblViewCategory.keepPreviousCellExpanded = false

        self.LoadData()
    }


    func LoadData() {

        var cellInfo = CellInfo("SubCategory 1",SubCount: "10")

        let sec1 = SectionInfo("Category 1", SubCount: "5", Image: UIImage(named: "Category3")!)
        sec1.cells.append(cellInfo)
        let sec2 = SectionInfo("Category 2", SubCount: "8", Image: UIImage(named: "Category3")!)
        cellInfo = CellInfo("SubCategory 2",SubCount: "20")
        sec2.cells.append(cellInfo)
        cellInfo = CellInfo("SubCategory 2.1",SubCount: "30")
        sec2.cells.append(cellInfo)

        let sec3 = SectionInfo("Category 3", SubCount: "10", Image: UIImage(named: "Category3")!)
        cellInfo = CellInfo("SubCategory 3",SubCount: "30")
        sec3.cells.append(cellInfo)

        tableViewData.append(sec1)
        tableViewData.append(sec2)
        tableViewData.append(sec3)



        let celNib = UINib.init(nibName: "SubCategoryCell", bundle: nil)
        tblViewCategory.register(celNib, forCellReuseIdentifier: "SubCategoryCell")

        let headerNib = UINib.init(nibName: "HeaderView", bundle: nil)
        tblViewCategory.register(headerNib, forHeaderFooterViewReuseIdentifier: "HeaderView")
    }


    @IBAction func DrawerMenutap(_ sender: Any) {

        self.OpenDrawerMenu()
    }


    func tableView(_ tableView: JExpandableTableView, numberOfRowsInSection section: Int, callback: @escaping (Int) -> Void) {

        let sectionInfo = self.tableViewData[section]
        callback(sectionInfo.cells.count)
    }

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

    func tableView(_ tableView: JExpandableTableView, heightForRowAtIndexPath indexPath: IndexPath) -> CGFloat {
        return 44
    }

    func tableView(_ tableView: JExpandableTableView, initialNumberOfRowsInSection section: Int) -> Int {

     //   let sectionInfo = self.tableViewData[section]
        return 0
    }

    func numberOfSections(in tableView: JExpandableTableView) -> Int {
        return tableViewData.count
    }

    func tableView(_ tableView: JExpandableTableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{

        let section = tableViewData[indexPath.section]
        let row = section.cells[indexPath.row]

        let cell = tableView.dequeueReusableCell(withIdentifier: "SubCategoryCell", for: indexPath) as! SubCategoryCell

        cell.contentView.backgroundColor = UIColor.white
        cell.lblSubCategoryName.text = row.SubcategoryName
        return cell
    }


    func tableView(_ tableView: JExpandableTableView, viewForHeaderInSection section: Int) -> UIView? {

        let section = self.tableViewData[section]
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderView") as! HeaderView
        header.contentView.backgroundColor = UIColor.groupTableViewBackground
        header.CatName.text = section.CategoryName
        header.CatImgView.image = UIImage(named: "Category4")
        header.CatCount.text = section.CategoryCount
        return header
    }




}


//MARK: Table View Cell for Category

class CatCell: UITableViewCell {

    @IBOutlet weak var lblName: UILabel!
}

My further requirement is I want to expand the Cells (Subcategory 2, SubCategory 2.1) in order to accommodate SubSubCategory(Childrens of SubCategory) in Case if they Exist. So what should be the approach for achieving this.


回答1:


UITableView is really designed in a way to show two levels, sections and rows.

But to show more then two levels you can manipulate rows that will increase/expand or decrease/collapse according to your model for Section, SubCategory.

So table structure will look like that

 section_header_1
    subCategory_1.0
        subSubCategory_1.0.1
    subCategory_1.1
        subSubCategory_1.1.1
    subCategory_1.2
        subSubCategory_1.2.1

 section_header_2
    subCategory_2.0
        subSubCategory_2.0.1
    subCategory_2.1
        subSubCategory_2.1.1
    subCategory_2.2
        subSubCategory_2.2.1

For Header you have to make your own custom header row and put that as the first row of each section. You could set up a cell to LOOK like a header, and setup the tableView:didSelectRowAt to manually expand or collapse the section, subCategory or SubSubCategory it is in. the rows after first row will be your subCategory and subSubCategory.

Then a Model For Section, SubCategory and SubSubCategory to store a booleans corresponding the the "expend" value of each of your sections, subCategories. you can avoid SubSubCategory model if it's only store it's name but it's easy to understand if you do so. for an example a struct for holding Section, SubCategory "expend" booleans.

public struct Section {
    var name: String
    var expand: Bool
    var subCategory:[SubCategory]

    public init(name: String, expand: Bool = false ,subCategory: [SubCategory]) {
        self.name = name
        self.expand = expand
        self.subCategory = subCategory
    }
}
public struct SubCategory {
    var name: String
    var expand: Bool
    var subSubCategory: SubSubCategory
    public init(name: String, expand: Bool = false, subSubCategory: SubSubCategory) {
        self.name = name
        self.expand = expand
        self.subSubCategory = subSubCategory
    }
}
public struct SubSubCategory {
    var name: String
    public init(name: String) {
        self.name = name
    }
}

Create 3 Custom cell one for Header other for SubCategory and SubSubCategory and display it in the first row of every section Header Cell and after expand or collapse show your SubCategory or SubSubCategory Cell accordingly.

after all together your code should be look that that.

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!
    var sampleData: [Section] = [
        Section(name: "Category 1", expand: false,
                subCategory: [

                    SubCategory(name: "Category 1.1", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 1.1.1")),

                    SubCategory(name: "Category 1.2", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 1.2.1"))
            ]
        ),
        Section(name: "Category 2", expand: false,
                subCategory: [

                    SubCategory(name: "Category 2.1", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 2.1.1")),

                    SubCategory(name: "Category 2.2", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 2.2.1"))
            ]
        )
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //
    // MARK: - View Controller DataSource and Delegate
    //

    func numberOfSections(in tableView: UITableView) -> Int {
        return sampleData.count
    }

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

        var expandCount = 0
        if sampleData[section].expand {
            // if header is expanded all subCategory will be also expanded
            expandCount = sampleData[section].subCategory.count
            for subCategory in sampleData[section].subCategory{
                //check for how many subSubCategory is expanded
                if subCategory.expand{
                    expandCount += 1
                }
            }
        }

        // returning the count of total expanded SubCategories and SubSubCategories
        // 1 is for header you can remove if you are using `viewForHeaderInSection`
        return 1 + expandCount
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Header cell
        if indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "header")!
            return cell
        }else{

            var countValue = 0
            var indexSubCategory = 0
            let sampleDataSection = sampleData[indexPath.section]

            // check for how many "subCategory" expanded or collapsed
            if sampleDataSection.expand{
                for (index, subCategory) in sampleDataSection.subCategory.enumerated(){

                    countValue += 1
                    if countValue >= indexPath.row{
                        indexSubCategory = index
                        break
                    }
                    // check for how many "subSubCategory" expanded or collapsed
                    if subCategory.expand{
                        if index == sampleDataSection.subCategory.count-1{
                            countValue += 2
                            indexSubCategory = index + 1
                        }else{
                            countValue += 1
                        }
                    }
                }

                // if countValue is grater then indexPath.row it will return "subSubCategory" cell
                // else/countValue = indexPath.row then return "subCategory" cell

                if countValue > indexPath.row{
                    // Cell subSubCategory
                    let cell = tableView.dequeueReusableCell(withIdentifier: "subSubCategory")!
                    cell.textLabel?.text = self.sampleData[indexPath.section].subCategory[indexSubCategory - 1].subSubCategory.name
                    return cell
                }else{
                    // Cell subCategory
                    let cell = tableView.dequeueReusableCell(withIdentifier: "subCategory")!
                    cell.textLabel?.text = self.sampleData[indexPath.section].subCategory[indexSubCategory].name
                    return cell
                }
            }

            else{
                // Cell subCategory
                let cell = tableView.dequeueReusableCell(withIdentifier: "subCategory")!
                cell.textLabel?.text = self.sampleData[indexPath.section].subCategory[indexPath.row].name
                return cell
            }
        }
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // then header cell is selected switch between collapse or expand between "subCategory"
        if indexPath.row == 0{
            let expand = !sampleData[indexPath.section].expand

            //Toggle collapse
            sampleData[indexPath.section].expand = expand
            self.tableView.reloadSections([indexPath.section], with: .none)
        }else{
            var countValue = 0
            var indexSubCategory = 0
            let sampleDataSection = sampleData[indexPath.section]
            if sampleDataSection.expand{
                for (index, subCategory) in sampleDataSection.subCategory.enumerated(){

                    countValue += 1
                    if countValue >= indexPath.row{
                        indexSubCategory = index
                        break
                    }
                    if subCategory.expand{
                        if index == sampleDataSection.subCategory.count-1{
                            countValue += 2
                            indexSubCategory = index + 1
                        }else{
                            countValue += 1
                        }
                    }
                }
                // and if "subCategory" cell is selected switch between collapse or expand between "subSubCategory"
                if countValue == indexPath.row{
                    let subSubCategory = sampleData[indexPath.section].subCategory[indexSubCategory]
                    let expand = !subSubCategory.expand
                    sampleData[indexPath.section].subCategory[indexSubCategory].expand = expand
                    UIView.performWithoutAnimation {
                        self.tableView.reloadSections([indexPath.section], with: .none)
                        self.tableView.layoutIfNeeded()
                    }
                }
            }
        }
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }

    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return CGFloat.leastNormalMagnitude
    }

}

Download demo project from here




回答2:


You can achieve this by creating a custom cell for the SubCategory 2.1, and displaying it in the first row of every section. Than, in the didSelectRow method, if the first row is selected, you update the state of the SubCategory 2.1 to collapsed (or non collapsed),reload the section and in the numberOfRows method you should return the appropriate number of rows according to the state of the SubCategory 2.1.




回答3:


A UITableView really isn't designed to show more than two levels of a hierarchy, as sections and rows.

If you want to show more than two levels there are all deferent type of custom solutions to this, one easy way is to design different cell for Subcategory and display accordingly like showed in the image bellow.

In the above example it is same cell but with different background colour to indicate subcategory.




回答4:


I came across this video on youtube :

https://www.youtube.com/watch?v=VFtsSEYDNRU

It might help you to achieve what you want.

But I think that you could achieve this by making a custom UIView and putting it into your cell so that when you tap on the subcategory it expands and shows more detail as you wanted it.

Hope this helps.



来源:https://stackoverflow.com/questions/50859675/expanding-the-expandable-table-view-cells-to-further-extent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!