xmartlabs / XLForm

XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.
MIT License
5.77k stars 953 forks source link

Collapse/Uncollapse Section But Leave The Section Header #962

Closed Marcin-Kapusta closed 7 years ago

Marcin-Kapusta commented 7 years ago

Hi. I added custom section header UIView with show/hide button. My goal is to hide/show section cells but leave the section header so it is always visible and I can control visibility of the section cells.

How can I implement hiding/showing only rows in this section. Currently I have this in my XLFormViewController:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerNib = UINib(nibName: "CollapsableHeaderView", bundle: nil)
        let headerView: CollapsableHeaderView = UIView.instance(fromNib: headerNib)!

        let sectionRow = self.form.formSections.object(at: section) as! XLFormSectionDescriptor
        headerView.headerTitle.text = sectionRow.title?.uppercased()
        headerView.section = section
        headerView.delegate = self
        return headerView
    }

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

    func showCellsInSection(_ section: Int) {
        print("show cells in section: \(section)")
        let sectionRow = self.form.formSections.object(at: section) as! XLFormSectionDescriptor
        for row in sectionRow.formRows as NSArray as! [XLFormRowDescriptor] {
            row.hidden = false
        }
    }

    func hideCellsInSection(_ section: Int) {
        print("hide cells in section: \(section)")

        let sectionRow = self.form.formSections.object(at: section) as! XLFormSectionDescriptor
        for row in sectionRow.formRows as NSArray as! [XLFormRowDescriptor] {
            row.hidden = true
        }
    }

Hiding is working very well but after hiding all cells in section there is no working way to show them again. Am I doing something wrong here? It could be really nice if sections should have additional option collapsable which works like on the Mail app on iPhone in iOS 10 on Mailboxes View Controller.

m-revetria commented 7 years ago

Hi, @Marcin-Kapusta

Showing the rows is not working because the property XLFormSectionDescriptor.formRows only returns the visible rows. Currently there is not a supported way to access to all the rows including the hidden ones. You can use next code as workaround to access all the rows from your form, but noticed that this is not supported and may change in the future:

@interface XLFormSectionDescriptor (_XLFormDescriptor)
@property NSArray * allRows;
@end

// Later you can do
NSArray rows = [[formSection allRows] copy];

In my opinion the best approach is to make a new row with a bool value instead of your current section header and associate the hidden properties of the other rows to its value. You can see how to do this in some of the provided examples, like BasicPredicateViewController.m

You can find the documentation in this link https://github.com/xmartlabs/XLForm#make-a-row-or-section-invisible-depending-on-other-rows-values

Marcin-Kapusta commented 7 years ago

Hi, @m-revetria

Thank You very much for the answer. I already made this using my solution with custom header approach. My solution is based on additional dictionary variable that keeps reference to all rows in form. I'm filling this dictionary during initializeForm phase with form initialization. This way I have reference to the rows even when they are hidden. Take a look at my solution. The methods hide... and show are called by delegation from header button cell. I think this is simple solution. Works very well on iPhone and iPad.

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "collapsableHeader") as! CollapsableHeaderView

        let sectionRow = self.form.formSections.object(at: section) as! XLFormSectionDescriptor
        headerView.section = section
        headerView.collapsed = sectionRow.formRows.count == 0
        headerView.delegate = self
        return headerView
    }

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

    var sections: [Int: [XLFormRowDescriptor]] = [:]

    func showCellsInSection(_ section: Int) {
        for row in sections[section]! {
            row.hidden = false
        }
    }

    func hideCellsInSection(_ section: Int) {
        for row in sections[section]! {
            row.hidden = true
        }
    }