Swift 3 Sectioned NSFetchResultController

时间:2016-10-27 04:47:03

标签: ios swift3 xcode8

我试图让部分在我的NSFetchResultController中工作(使用Swift 3)。这些部分的计算属性为NSManagedObject。这些部分显示得很好。但是每当我尝试添加一个新的Object时会生成一个新的部分,应用程序会崩溃并出现以下错误:

  

由于未捕获的异常终止应用程序' NSInternalInconsistencyException',原因:'无效更新:第4节中的无效行数。更新后现有部分中包含的行数(1)必须等于更新前的该部分中包含的行数(1),加上或减去从该部分插入或删除的行数(插入1个,删除0个),加上或减去移入的行数或者超出该部分(0移入,0移出)。'

我的TableViewController的FetchResultController部分如下所示:

lazy var fetchedResultsController: NSFetchedResultsController<Package> = {
    let fetchRequest = NSFetchRequest<Package>(entityName:"Package")

    let sectionDescriptor = NSSortDescriptor(key: "sectionTitle", ascending: true)
    let sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: true)
    fetchRequest.sortDescriptors = [sectionDescriptor , sortDescriptor]

    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "sectionTitle", cacheName: nil)

    fetchedResultsController.delegate = self

    return fetchedResultsController
}()

override func viewDidLoad() {
    super.viewDidLoad()

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    managedObjectContext = appDelegate.persistentContainer.viewContext

    do {
        try self.fetchedResultsController.performFetch()
    } catch {
        let fetchError = error as NSError
        print("\(fetchError), \(fetchError.userInfo)")
    }
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Package")

    do {
        try managedObjectContext.fetch(fetchRequest)
    } catch let error as NSError {
        print("Could not fetch \(error), \(error.userInfo)")
    }
}

TableViewController的TableView数据源部分如下所示:

override func numberOfSections(in tableView: UITableView) -> Int {
    if let sections = fetchedResultsController.sections {
        return sections.count
    }
    return 0
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if let sections = fetchedResultsController.sections {
        let sectionInfo = sections[section]
        return sectionInfo.numberOfObjects
    }
    return 1
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    if let sections = fetchedResultsController.sections {
        let currentSection = sections[section]
        return currentSection.name
    }

    return nil
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier: String = "PackageTableCell"
    let cell: PackageTableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! PackageTableViewCell
    let package = fetchedResultsController.object(at: indexPath)

    cell.labelId.text = String(describing: package.packageId)
    cell.labelTime.text = DateFormatter.localizedString(from: package.deliveryTime as! Date, dateStyle: DateFormatter.Style.none, timeStyle: DateFormatter.Style.short)
    cell.labelWeight.text = String(describing: package.weight)
    cell.labelBatch.text = String(describing: package.batch!.batchId)
    cell.labelRecepients.text = String(describing: package.recepients)

    return cell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let package: Package = fetchedResultsController.object(at: indexPath)
        managedObjectContext.delete(package)
    }
}

我的TableViewController的FetchResultController委托部分如下所示:

func controllerWillChangeContent() {
    tableView.beginUpdates()
}

func controllerDidChangeContent() {
    tableView.endUpdates()
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    switch type {
    case .insert:
        tableView.insertSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
    case .delete:
        tableView.deleteSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
    case .move:
        break
    case .update:
        break
    }
}


func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch (type) {
    case .insert:
        tableView.insertRows(at: [newIndexPath!], with: .fade)
    case .delete:
        tableView.deleteRows(at: [indexPath!], with: .fade)
    case .update:
        let cell = tableView.cellForRow(at: indexPath!) as! PackageTableViewCell
        configureCell(cell: cell, atIndexPath: indexPath! as NSIndexPath)
    case .move:
        tableView.moveRow(at: indexPath!, to: newIndexPath!)
    }
}

然后在我的AddViewController中,我保存对象,如下所示:

@IBAction func savePressed(_ sender: UIBarButtonItem) {

    if mode == .add {
        let entity =  NSEntityDescription.entity(forEntityName: "Package", in: self.managedObjectContext)
        package = Package(entity: entity!, insertInto: self.managedObjectContext)

        let packageId: Int = UserDefaults.standard.integer(forKey: "packageId")
        package?.packageId = Int32(packageId)
        UserDefaults.standard.set((packageId +1), forKey: "packageId")

        package?.createdAt = NSDate()
        package?.location = mapView.userLocation.location! as CLLocation
    }

    let calendar = NSCalendar.current

    let selectedDate = pickerDate.date
    let selectedTime = pickerTime.date

    var deliveryTimeComponents = DateComponents()
    deliveryTimeComponents.year = calendar.component(.year, from: selectedDate)
    deliveryTimeComponents.month = calendar.component(.month, from: selectedDate)
    deliveryTimeComponents.day = calendar.component(.day, from: selectedDate)
    deliveryTimeComponents.hour = calendar.component(.hour, from: selectedTime)
    deliveryTimeComponents.minute = calendar.component(.minute, from: selectedTime)
    let deliveryTime = calendar.date(from: deliveryTimeComponents)

    package?. deliveryTime = deliveryTime as NSDate?
    package?.batch = batchPicker?.selectedBatch
    package?.weight = Float(inputWeight.text!)!
    package?.recepients = Int16(stepperRecepients.value)
    package?.notes = textNotes.text!

    do {
        try package?.managedObjectContext?.save()
        if mode == .edit {
            packageTransferDelegate?. packageTransfer(transferPackage: package!)
        }
        dismiss(animated: true, completion: nil)
    } catch let error as NSError  {
        print("Could not save \(error), \(error.userInfo)")
    }

}

最后我的Package Class(Package + CoreDataClass)

import Foundation
import CoreData

public class Package: NSManagedObject {

var sectionTitle: String {
    return DateFormatter.localizedString(from: self.deliveryTime! as Date, dateStyle: DateFormatter.Style.long, timeStyle: DateFormatter.Style.none)
}
}

有关如何解决此问题的任何提示都将非常受欢迎。我也是iOS开发的新手,所以如果你在我的代码中看到你不喜欢的东西,请随时给我写一下

1 个答案:

答案 0 :(得分:0)

我找到了自己问题的答案。我忘了将NSFetchResultContoller传递给controllerWillChangeContentcontrollerDidChangeContent函数。导致崩溃的原因。

我更正了代码如下:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.beginUpdates()
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
}

现在一切似乎都运转得很好。