简体   繁体   English

无效更新:UITableView 中的节数无效

[英]Invalid update: invalid number of sections in UITableView

I'm not finding an answer to the issue that I'm having.我没有找到我遇到的问题的答案。 I'm using NSFetchedResultsControllerDelegete to connect my table view with my data.我正在使用 NSFetchedResultsControllerDelete 将我的表格视图与我的数据连接起来。 I'm retrieving the data with sectionNameKeyPath set so that I can group items in the table view.我正在检索设置了 sectionNameKeyPath 的数据,以便我可以在表视图中对项目进行分组。 If I have only one item in a section of the table view and delete it, then I get the following error:如果我在表格视图的一部分中只有一个项目并将其删除,那么我会收到以下错误:

Invalid update: invalid number of sections.无效更新:无效的节数。 The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).更新后表视图中包含的节数(1)必须等于更新前表视图中包含的节数(2),加上或减去插入或删除的节数(0插入,0已删除)。

Here is my screen before the delete:这是我删除前的屏幕:

在此处输入图像描述

Here's my code for the view:这是我的视图代码:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM