Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expand/collapse UITableView sections with a backing NSFetchedResultsController

    let frc = NSFetchedResultsController(
        fetchRequest: alertsFetchRequest,
        managedObjectContext: self.moc,
        sectionNameKeyPath: "formattedDateDue",
        cacheName: nil)

How can I expand and collapse the sections on my table view when I've used the NSFetchedResultsController to section my records?

I have seen a lot of tutorials that explain expanding and collapsing cells themselves but not anything on sections produced using the fetched results controller.

like image 622
renx Avatar asked Apr 08 '16 19:04

renx


1 Answers

First, you need an array to keep track of whether each section is expanded or collapsed:

var sectionExpandedInfo : [Bool] = []

After the fetched results controller has done its initial performFetch, populate this array with true for each section (assuming you want sections expanded by default):

sectionExpandedInfo = []
for _ in frc.sections! {
    sectionExpandedInfo.append(true)
}

Amend the numberOfRowsInSection method to return zero if the section is collapsed:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if sectionExpandedInfo[section] { // expanded
        let sectionInfo = self.frc.sections![section]
        return sectionInfo.numberOfObjects
    } else { // collapsed
        return 0
    }
}

To toggle whether a section is expanded or not, I've used a button as the viewForHeaderInSection, with the section name as the title:

override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    if (self.frc.sections!.count > 0) {
        let sectionInfo = self.frc.sections![section]
        let sectionHeaderButton = UIButton(type: .Custom)
        sectionHeaderButton.backgroundColor = UIColor.redColor()
        sectionHeaderButton.setTitle(sectionInfo.name, forState: .Normal)
        sectionHeaderButton.addTarget(self, action: #selector(MasterViewController.toggleSection(_:)), forControlEvents: .TouchUpInside)
        return sectionHeaderButton
    } else {
        return nil
    }
}

and in the toggleSection method I then use the title to determine which header button has been tapped, and expand/collapse the corresponding section:

func toggleSection(sender: UIButton) {
    for (index, frcSection) in self.frc.sections!.enumerate() {
        if sender.titleForState(.Normal) == frcSection.name {
            sectionExpandedInfo[index] = !sectionExpandedInfo[index]
            self.tableView.reloadSections(NSIndexSet(index: index), withRowAnimation: .Automatic)
        }
    }
}

If your FRC inserts or deletes sections, you need to update the sectionExpandedInfo array to include/remove the extra section:

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
    switch type {
        case .Insert:
            self.sectionExpandedInfo.insert(true, atIndex: sectionIndex)
            self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
        case .Delete:
            self.sectionExpandedInfo.removeAtIndex(sectionIndex)
            self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
        default:
            return
    }
}

Again this assumes you want sections expanded by default.

like image 166
pbasdf Avatar answered Nov 20 '22 02:11

pbasdf