在同一个 UITableView 的不同部分显示多个 NSFetchedResultsController

Posted

技术标签:

【中文标题】在同一个 UITableView 的不同部分显示多个 NSFetchedResultsController【英文标题】:Displaying multiple NSFetchedResultsControllers in different sections of same UITableView 【发布时间】:2016-01-12 14:38:16 【问题描述】:

早上好。 在我的应用程序中,我有不同的实体,我想在同一个 TableView 中显示它。 我知道要做到这一点,我需要不同的 NSFetchedResultsController。 当我添加一个对象做第一部分时,没有问题出现,但是当我将一个对象添加到第二部分时,问题就出现了。 这是我正在使用的代码

import UIKit
import CoreData

class MainMenu: UITableViewController, NSFetchedResultsControllerDelegate 
    /**********/
    /** Vars **/
    /**********/

    lazy var frcAccount: NSFetchedResultsController = self.AccountFetchedResultController()
    lazy var frcCar: NSFetchedResultsController = self.CarFetchedResultsController()

    let idxAccNew = NSIndexPath(forRow: 0, inSection: 0)
    let idxCarNew = NSIndexPath(forRow: 0, inSection: 1)
    let idxConfig = NSIndexPath(forRow: 0, inSection: 2)

    /**************/
    /** IBOutlet **/
    /**************/

    /***************/
    /** IBActions **/
    /***************/

    /***************/
    /** Functions **/
    /***************/

     // MARK:- NSFetchedResultsController
    func AccountFetchedResultController() -> NSFetchedResultsController 
        let fetchRequest = NSFetchRequest(entityName: Account.entityName)
        fetchRequest.fetchBatchSize = 10

        let sortDescriptor = NSSortDescriptor(key: "id", ascending: false)
        fetchRequest.sortDescriptors = [sortDescriptor]


        frcAccount = NSFetchedResultsController(fetchRequest: fetchRequest,
            managedObjectContext: coreDataStack.context,
            sectionNameKeyPath: nil,
            cacheName: nil)
        frcAccount.delegate = self

        do 
            try frcAccount.performFetch()
         catch 
            print("Erro: \(error)")
            abort()
        

        return frcAccount
    
    func CarFetchedResultsController() -> NSFetchedResultsController 
        let fetchRequest = NSFetchRequest(entityName: "Car")
        fetchRequest.fetchBatchSize = 10

        let sortDescriptor = NSSortDescriptor(key: "plate", ascending: false)
        fetchRequest.sortDescriptors = [sortDescriptor]

        frcCar = NSFetchedResultsController(fetchRequest: fetchRequest,
            managedObjectContext: coreDataStack.context,
            sectionNameKeyPath: nil,
            cacheName: nil)
        frcCar.delegate = self

        do 
            try frcCar.performFetch()
         catch 
            print("Error: \(error)")
            abort()

        
        return frcCar
    

    //MARK:- TableViewDataSource
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return 3
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        switch section
        case 0:
            return frcAccount.sections![0].numberOfObjects + 1
        case 1:
            return frcCar.sections![0].numberOfObjects + 1
        default:
            return 1
        
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        if indexPath.section == 0 
            if indexPath.row == 0 
                let cell = tableView.dequeueReusableCellWithIdentifier("Cell") //as UITableViewCell
                cell!.textLabel?.text = "Nova Conta"

                return cell!
             else 
                let cell = tableView.dequeueReusableCellWithIdentifier("CellAccount") as! VC_AccountListTableViewCell
                let idx = NSIndexPath(forRow: indexPath.row - 1, inSection: 0)
                let object = frcAccount.objectAtIndexPath(idx) as! Account

                cell.useAccountInfo(object)

                return cell
            
         else if indexPath.section == 1
            if indexPath.row == 0 
                let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
                cell!.textLabel?.text = "Novo Carro"

                return cell!
             else 
                let cell = tableView.dequeueReusableCellWithIdentifier("CellCar") as! VC_CarListTableViewCell
                let idx = NSIndexPath(forRow: indexPath.row - 1, inSection: 0)
                let object = frcCar.objectAtIndexPath(idx) as! Car

                cell.useCarInfo(object)
                cell.btnFillUp.tag = indexPath.row - 1
                cell.btnService.tag = indexPath.row - 1

                return cell
            
         else 
            let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
            cell!.textLabel?.text = "Configurações"

            return cell!
        
    
    // MARK: TableViewDelegate
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 
        if indexPath == idxAccNew || indexPath == idxCarNew || indexPath == idxConfig 
            return 44
         else 
            if indexPath.section == 0 
                return 80
             else if indexPath.section == 1 
                return 100
             else 
                return 0
            
        
    
    // MARK:- NSFetchedResultsControllerDelegate
    func controllerWillChangeContent(controller:NSFetchedResultsController) 
        tableView.beginUpdates()
    
    func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) 
        switch(type) 
        case .Insert:
            tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
        case .Delete:
            tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
        default:
            return
        
    
    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) 

        switch (type) 
        case .Insert:
            tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
        case .Delete:
            tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
        case .Move:
            tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
        case .Update:
            tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
        

    
    func controllerDidChangeContent(controller: NSFetchedResultsController) 
        tableView.reloadData()
        tableView.endUpdates()

    

    func reloadData() 
        tableView.reloadData()
    



这样可以吗??我尝试了很多不同的解决方案..

谢谢大家

【问题讨论】:

【参考方案1】:

在 tableView 数据源方法中,您正确地将 tableView 的 indexPath 重新映射到相关 FRC 的 indexPath,同时还调整了额外的第一行,例如:

let idx = NSIndexPath(forRow: indexPath.row - 1, inSection: 0)

(请注意,您必须在其他 tableView 数据源委托方法中使用类似的重新映射,例如,如果您实现 didSelectRowAtIndexPath)。但是您还需要在 FRC 委托方法中进行反向映射,例如。

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) 
    var tvIdx = NSIndexPath()
    var newTvIdx = NSIndexPath()
    var tvSection = 0
    // for the Car FRC, we need to insert/delete rows in table view section 1:
    if (controller == self.frcCar) 
        tvSection = 1
    
    // adjust the indexPaths (indexPath and newIndexPath)
    // to add the extra row, and use the correct tableView section:
    if let idx = indexPath 
        tvIdx = NSIndexPath(forRow: idx.row + 1, inSection: tvSection)
    
    if let newIdx = newIndexPath 
        newTvIdx = NSIndexPath(forRow: newIdx.row + 1, inSection: tvSection)
    
    switch (type) 
        case .Insert:
            tableView.insertRowsAtIndexPaths([newTvIdx], withRowAnimation: .Fade)
        case .Delete:
            tableView.deleteRowsAtIndexPaths([tvIdx], withRowAnimation: .Fade)
        case .Move:
            tableView.deleteRowsAtIndexPaths([tvIdx], withRowAnimation: .Fade)
            tableView.insertRowsAtIndexPaths([newTvIdx], withRowAnimation: .Fade)
        case .Update:
            tableView.reloadRowsAtIndexPaths([tvIdx], withRowAnimation: .Fade)
    

那么就不需要在controllerDidChangeContent中使用reloadData了:

func controllerDidChangeContent(controller: NSFetchedResultsController) 
    tableView.endUpdates()

【讨论】:

以上是关于在同一个 UITableView 的不同部分显示多个 NSFetchedResultsController的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 部分标题与 UITableviewCell 重叠并且没有背景颜色

UITableView didSelectRowAtIndexPath ,与部分行中显示的值不同!

iPhone - 帮助在 UITableView 中添加 9 个不同的部分

UITableView 分组数组

UITableView 中的突出显示按钮反应缓慢

带有 NSFetchedResultsController 的 UITableView:彼此独立地对每个部分进行排序