1

Following the answer of jeantimex on how to expand/collapse a section here and his github, I added the following codes to hide the rows when a section is tapped:

struct Person {

    //this is my data

    let name: String
    var item: [(itemName: String, price: Decimal)]
    var collapsed: Bool!

    init(name: String, item: [(itemName: String, price: Decimal)], collapsed: Bool = false) {
        self.name = name
        self.item = item
        self.collapsed = collapsed
    }
}

class TableSectionHeader : UITableViewHeaderFooterView {

    //this is my custom header section

    var delegate: CollapsibleTableViewHeaderDelegate?
    var section: Int = 0

    @IBOutlet weak var lblPerson: UILabel!
    @IBOutlet weak var lblTotal: UILabel!

    func tapHeader(_ gestureRecognizer: UITapGestureRecognizer) {

        guard let cell = gestureRecognizer.view as? TableSectionHeader else {
            return
        }

        delegate?.toggleSection(self, section: cell.section)
        print(cell.section)
    }
}

protocol CollapsibleTableViewHeaderDelegate {

    func toggleSection(_ header: TableSectionHeader, section: Int)
}

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

    return personArray[indexPath.section].collapsed! ? 0 : 44.0
}

In my viewForHeaderInSection delegate, I added a UITapGestureRecognizer:

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

    let cell = billTableView.dequeueReusableHeaderFooterView(withIdentifier: "TableSectionHeader")
    let header = cell as! TableSectionHeader

    header.section = section
    header.delegate = self
    header.lblPerson.text = structArray[section].name
    header.lblTotal.text = SplitBill().displayIndividualTotal(person: structArray, section: section)
    header.addGestureRecognizer(UITapGestureRecognizer(target: header.self, action: #selector(header.tapHeader(_:))))
    return cell
}

The sections are able to collapse/expand perfectly, however I have a button to remove a section, and when I removed the first section (0) and tried to tap on another section, the app crashed with error:

fatal error: Index out of range

I did some debugging to print the section index and realised that when I removed an object from my data, the toggleSection function was still holding the second section's index (1):

extension BillForm: CollapsibleTableViewHeaderDelegate {

    func toggleSection(_ header: TableSectionHeader, section: Int) {

        print(section) //index is 1 although I have removed the first object
        let collapsed = !personArray[section].collapsed

        // Toggle collapse
        personArray[section].collapsed = collapsed

        // Adjust the height of the rows inside the section
        billTableView.beginUpdates()
        for i in 0 ..< personArray[section].item.count {

            billTableView.reloadRows(at: [IndexPath(row: i, section: section)], with: .automatic)
        }

        billTableView.endUpdates()
    }
}

I'm still a little confused and not very familiar with jeantimex's code so I'm not sure where to fix this. Can anyone help me?

EDIT:

Managed to got it working with reloadData() on my remove section button.

for i in (0..<self.personArray[row].item.count).reversed() {

    let rowIndex = IndexPath(row: i, section: row)
    self.personArray[row].item.remove(at: i) //remove rows first
    self.billTableView.deleteRows(at: [rowIndex], with: .right)
}

self.personArray.remove(at: row) //then remove section
self.billTableView.deleteSections(IndexSet(integer: row), with: .right)
self.billTableView.reloadData()
Community
  • 1
  • 1
iamhx
  • 472
  • 6
  • 24
  • I noticed that you're getting the section from the header `cell.section` - could it be that because headers are being reused that you are not updating that section when collapsing? – sooper Apr 22 '17 at 14:25
  • @sooper Now that you mention it, I think it might be the case. The `cell.section` is not updating it's index when I removed the first object.. – iamhx Apr 22 '17 at 14:32

1 Answers1

1

You're reusing the header views but not updating section to correspond to the new data source structure. You'll need to update the header views that are visible after collapsing/expanding.

sooper
  • 5,991
  • 6
  • 40
  • 65
  • I added a `reloadData()` to my tableView after removing the section and it works now... – iamhx Apr 22 '17 at 14:44
  • That's another way, though I'm not sure you'll still get the animations you want when collapsing? – sooper Apr 22 '17 at 14:49
  • Hmm, yes there seems to be one problem with the animations when I remove the first section... The second section moves up only instead of both section and row. – iamhx Apr 22 '17 at 14:55
  • Thought so - you'll need to iterate through the visible cells and reconfigure them to preserve animations – sooper Apr 22 '17 at 14:56
  • how do I do so? I edited my question to include my remove section code. – iamhx Apr 22 '17 at 15:01