Ios 使用支持NSFetchedResultsController展开/折叠UITableView节

Ios 使用支持NSFetchedResultsController展开/折叠UITableView节,ios,uitableview,core-data,Ios,Uitableview,Core Data,当我使用NSFetchedResultsController对记录进行分区时,如何展开和折叠表视图上的分区 我看过很多教程,这些教程解释了如何展开和折叠单元格本身,但对使用fetched results controller生成的部分没有任何解释。如果要更改结果集,需要更改请求中的谓词,然后再次调用performFetch()。然后,您可以更新您的表。但是,这可能会导致性能问题。您可以考虑其他更精细的技术来管理模型视图绑定,例如对于每个扩展的区段有不同的读取结果控制器。当用户展开一个节时,创建一

当我使用NSFetchedResultsController对记录进行分区时,如何展开和折叠表视图上的分区


我看过很多教程,这些教程解释了如何展开和折叠单元格本身,但对使用fetched results controller生成的部分没有任何解释。

如果要更改结果集,需要更改请求中的谓词,然后再次调用
performFetch()
。然后,您可以更新您的表。但是,这可能会导致性能问题。您可以考虑其他更精细的技术来管理模型视图绑定,例如对于每个扩展的区段有不同的读取结果控制器。当用户展开一个节时,创建一个新的fetchresults控制器,只获取该节的对象并更新表视图。当用户折叠节时,放弃“获取结果”控制器。但是,这会使表视图数据源实现变得相当复杂。

首先,您需要一个数组来跟踪每个部分是展开还是折叠:

    let frc = NSFetchedResultsController(
        fetchRequest: alertsFetchRequest,
        managedObjectContext: self.moc,
        sectionNameKeyPath: "formattedDateDue",
        cacheName: nil)
sectionExpandedInfo = []
for _ in frc.sections! {
    sectionExpandedInfo.append(true)
}
获取的结果控制器完成其初始
performFetch
后,使用每个节的
true
填充此数组(假设您希望默认情况下扩展节):

修改
numberofrowsinssection
方法,使其在节折叠时返回零:

    let frc = NSFetchedResultsController(
        fetchRequest: alertsFetchRequest,
        managedObjectContext: self.moc,
        sectionNameKeyPath: "formattedDateDue",
        cacheName: nil)
sectionExpandedInfo = []
for _ in frc.sections! {
    sectionExpandedInfo.append(true)
}
为了切换节是否展开,我使用了一个按钮作为
viewForHeaderInSection
,节名作为标题:

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
    }
}
toggleSection
方法中,我使用标题确定点击了哪个标题按钮,并展开/折叠相应的部分:

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
    }
}
如果FRC插入或删除节,则需要更新
sectionExpandedInfo
数组以包括/删除额外的节:

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)
        }
    }
}
同样,这假设您希望默认情况下展开节。

Swift 4: 以下是伟大解决方案的Swift 4版本:

定义和填充布尔数组:

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
    }
}
行数部分
方法:

sectionExpandedInfo = []
   for _ in _fetchedResultsController!.sections! {
      sectionExpandedInfo.append(true)
   }
在节标题中定义按钮(我必须将
切换选择
参数
\:
替换为
发送者:
,以使其适合我:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if sectionExpandedInfo[section] { // expanded
            let sectionInfo = self.fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
            return sectionInfo.numberOfObjects
        } else { // collapsed
            return 0
        }
    }
切换部分
功能:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    if (self.fetchedResultsController.sections!.count > 0) {
        let sectionInfo = self.fetchedResultsController.sections![section]
        let sectionHeaderButton = UIButton(type: .custom)
        sectionHeaderButton.backgroundColor = UIColor.red
        sectionHeaderButton.setTitle(sectionInfo.name, for: [])
        sectionHeaderButton.addTarget(self, action: #selector(MasterViewController.toggleSection(sender:)), for: .touchUpInside)
        return sectionHeaderButton
    } else {
        return nil
    }
}
插入或删除章节:

@objc func toggleSection(sender: UIButton) {
        for (index, frcSection) in self.fetchedResultsController.sections!.enumerated() {
            if sender.title(for: []) == frcSection.name {
                sectionExpandedInfo[index] = !sectionExpandedInfo[index]
                self.tableView.reloadSections(NSIndexSet(index: index) as IndexSet, with: .automatic)
            }
        }
    }
func控制器(控制器:NSFetchedResultsController,didChangeSection信息:NSFetchedResultsSectionInfo,索引节索引:Int,forChangeType类型:NSFetchedResultsChangeType){
开关类型{
案例.插入:
self.sectionExpandedInfo.insert(true,at:sectionIndex)
self.tableView.insertSections(NSIndexSet(索引:sectionIndex)作为IndexSet,带有:.fade)
案例.删除:
自节展开信息删除(位于:节索引)
self.tableView.deleteSections(NSIndexSet(索引:sectionIndex)作为索引集,带有:.fade)
违约:
返回
}
}

再次感谢

@uday.m您的编辑不是很好。请不要添加“Swift:”问题标题的前缀。效果很好!谢谢,@pbasdfGreat solution。谢谢。也许有一个改进。不是使用按钮标题来确定节,而是将sectionHeaderButton.tag=节添加到ViewForHeaderSection,然后在toggleSection中,您可以参考sender.tag来获取节号。@guido好主意。避免了节的笨重枚举。删除所有条目后,它可能会在
didchangesesection
中崩溃,因为索引随机进入会在
self.sectionExpandedInfo
上导致越界错误。我还没有解决方案。我已经获得了一个使用NSIndexSets而不是数组的工作版本。在
didchangesesection
中使用
NSMutableIndexSet:shiftIndexStartingAtIndex
用于说明由于插入或删除节而导致的索引更新(请参阅我的其他注释)。