无效更新:UITableView 中的部分数量无效

Ken*_*uch 4 core-data uitableview ios swift

我没有找到我遇到的问题的答案。我正在使用 NSFetchedResultsControllerDelegete 将我的表视图与我的数据连接起来。我正在使用 sectionNameKeyPath 设置检索数据,以便我可以在表视图中对项目进行分组。如果我在表视图的一部分中只有一个项目并将其删除,则会出现以下错误:

无效更新:节数无效。更新后表视图中包含的节数(1)必须等于更新前表视图中包含的节数(2),加上或减去插入或删除的节数(0插入,0删除)。

这是我删除前的屏幕:

在此处输入图片说明

这是我的视图代码:

import CoreData

class VehicleTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    var deleteItemIndexPath: IndexPath? = nil

    lazy var dao: DAOUtilities = {
        return DAOUtilities(context: GlobalVariables.getContext())
    }()

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

        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "activeFlag", ascending: false), NSSortDescriptor(key: "vehicleDesc", ascending: true)]

        // Initialize Fetched Results Controller
        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: GlobalVariables.getContext(), sectionNameKeyPath: "activeFlag", cacheName: nil)

        // Configure Fetched Results Controller
        fetchedResultsController.delegate = self

        return fetchedResultsController as! NSFetchedResultsController<Vehicles>
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

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

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

    // MARK: - Table view data source

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

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor(red: 55/255, green: 120/255, blue: 250/255, alpha: 1)

        guard let sectionInfo = fetchedResultsController.sections?[section] else {
            return view
        }
        let title = UILabel()
        title.font = UIFont.boldSystemFont(ofSize: 16)
        title.textColor = .white
        title.text = sectionInfo.name == "1" ? "Active" : "Inactive"
        view.addSubview(title)
        title.translatesAutoresizingMaskIntoConstraints = false
        title.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        title.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true

        return view
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "vehicleCell", for: indexPath)

        if indexPath.row % 2 == 0 {
            cell.backgroundColor = UIColor.clear
        }
        else {
            cell.backgroundColor = UIColor.lightGray.withAlphaComponent(0.2)
        }

        let vehicle = fetchedResultsController.object(at: indexPath)
        cell.textLabel!.text = vehicle.vehicleDesc

        return cell
    }

    // Override to support conditional editing of the table view.
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    // Override to support editing the table view.
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            deleteItemIndexPath = indexPath
            let vehicle = fetchedResultsController.object(at: indexPath)
            confirmDelete(itemToDelete: vehicle.vehicleDesc!)
        }
    }

    func confirmDelete(itemToDelete: String) {
        let alert = UIAlertController(title: "Delete Vehicle", message: "Are you sure you want to delete vehicle \(itemToDelete)", preferredStyle: .actionSheet)
        let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: handleDeleteItem)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: cancelDeleteItem)
        alert.addAction(deleteAction)
        alert.addAction(cancelAction)
        self.present(alert, animated: true, completion: nil)
    }

    func handleDeleteItem(alertAction: UIAlertAction!) -> Void {
        if let indexPath = deleteItemIndexPath {
            let vehicle = fetchedResultsController.object(at: indexPath)
            let route = dao.getRouteForVehicle(vehicleId: vehicle.vehicleId!)

            if let _ = route {
                vehicle.activeFlag = false
            }
            else {
                GlobalVariables.getContext().delete(vehicle)
            }
        }
    }

    func cancelDeleteItem(alertAction: UIAlertAction!) {
        deleteItemIndexPath = nil
    }

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

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

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

            if let newIndexPath = newIndexPath {
                tableView.insertRows(at: [newIndexPath], with: .fade)
            }
            break;
        default:
            break
        }
    }

    // MARK: - segue

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "editVehicleSegue" {
            if let vehicleEditViewController = segue.destination as? VehicleEditViewController {
                if let indexPath = tableView.indexPathForSelectedRow {
                    let vehicle = fetchedResultsController.object(at: indexPath)
                    vehicleEditViewController.vehicle = vehicle
                    vehicleEditViewController.tableView = tableView
                }
            }
        }
    }

//    // MARK: - Actions
//
//    @IBAction func btnAdd_ACTION(_ sender: UIBarButtonItem) {
//        var emptyFound = false
//        for i in 0..<vehicles.count {
//            let vehicle = vehicles[i]
//            if vehicle.isEmpty {
//                emptyFound = true
//                break
//            }
//        }
//        if !emptyFound {
//            vehicles.append("")
//            tableView.reloadData()
//        }
//    }
}
Run Code Online (Sandbox Code Playgroud)

Pau*_*w11 5

When you remove the last row from a section, you need to let the table view that the whole section has been removed.

You can do this by implementing

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, 
           didChange sectionInfo: NSFetchedResultsSectionInfo, 
      atSectionIndex sectionIndex: Int, 
                 for type: NSFetchedResultsChangeType) {

    let section = IndexSet(integer: sectionIndex)

    switch type {
        case .delete:
            tableView.deleteSections(section, with: .automatic)
        case .insert:
            tableView.insertSections(section, with: .automatic)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 大多数教程和示例仅使用单个部分,因此它们不涵盖此功能。 (2认同)