Displaying multiple NSFetchedResultsControllers in different sections of same UITableView

倖福魔咒の 提交于 2019-12-11 12:29:05

问题


good morning. In my App, I have different Entities and I want to show it in the same TableView. I know that to do it, I need different NSFetchedResultsController. When I add an object do the first section, no problem appears, but when I add an object to the second, the problem appears. Here is the code the I'm using

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()
    }


}

Is it possible to do this?? I have tried lots of different solutions..

Thanks all


回答1:


In the tableView datasource methods, you are correctly remapping the tableView's indexPath to the relevant FRC's indexPath, whilst also adjusting for the additional first row, eg:

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

(Note that you will have to use similar remapping in other tableView datasource delegate methods, e.g. if you implement didSelectRowAtIndexPath). But you also need to do the reverse mapping in the FRC delegate methods, eg.

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)
    }
}

There is then no need to use reloadData in controllerDidChangeContent:

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


来源:https://stackoverflow.com/questions/34746518/displaying-multiple-nsfetchedresultscontrollers-in-different-sections-of-same-ui

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!