ios - 无效更新: invalid number of sections in UITableView

标签 ios swift uitableview core-data

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

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

这是删除前我的屏幕:

enter image description here

这是我的 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()
//        }
//    }
}

最佳答案

当您从某个部分中删除最后一行时,您需要让表格看到整个部分已被删除。

您可以通过实现来做到这一点

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

关于ios - 无效更新: invalid number of sections in UITableView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58759291/

相关文章:

ios - 如何查询整个 Realm 数据库中的特定数据,而不仅仅是特定字段或属性

iOS Swift - 在 UIImage 上画线以保存到文件

ios - swift 项目的 Objective C opencv 包装器看不到 STL header

ios - 删除Eureka中单行的分隔线

ios - 是否可以在 UItableView 中有多个部分,即 : Section 1 has two sections and these two sections have multiple sections

ios - 如何设置滑动标签栏的y位置稍微向下

objective-c - Mediaplayer 没有专辑插图占位符

ios - 使用引号字符进行搜索时崩溃

swift - 不使用@objc 的 swift 可选协议(protocol)方法

ios - 如何为所有 View Controller 创建通用的UITableView?