Как исправить «NSInternalInconsistencyException», причина: «Неверное обновление: неверное количество строк в разделе 0.» - PullRequest
0 голосов
/ 06 июня 2019

Я создаю новое приложение для iOS, используя swift, и мне нужна помощь с получением моего tableView для удаления данных.Я использую Базовые Данные, чтобы сохранить данные в отношении «один ко многим», и у меня это так, что выбранная сущность удаляется, но происходит сбой при обновлении tableView.

Ранее я создавал приложения, но это мой первый случай использования метода хранения данных «один ко многим».У меня также есть решение для Google, но ни одно из них не сработало.Вот мой код для моего tableView editStyle.

if editingStyle == .delete {
    context.delete(items[indexPath.row])

    do {
        try context.save()

        self.tableView.beginUpdates()
        items.remove(at: indexPath.row)
        self.tableView.deleteRows(at: [indexPath], with: .fade)
        self.tableView.endUpdates()
    } catch let error as NSError {
        print("Could not save. \(error.localizedDescription)")
    }
}

Я хочу, чтобы строка удалялась вместе с сущностью в Базовых данных, но на самом деле она просто удаляет сущность, а затем вылетает при попытке обновить tableView.В частности, я думаю, что это происходит сбой, когда он вызывает «deleteRows», потому что затем выдает ошибку:

Project Aurora [14179: 303873] *** Завершение работы приложения из-за необработанного исключения «NSInternalInconsistencyException», причина:'Неверное обновление: недопустимое количество строк в разделе 0. Число строк, содержащихся в существующем разделе после обновления (2), должно быть равно количеству строк, содержащихся в этом разделе до обновления (2), плюс или минусколичество строк, вставленных или удаленных из этого раздела (0 вставлено, 1 удалено) и плюс или минус количество строк, перемещенных в или из этого раздела (0 перемещено, 0 перемещено). '

Подробнее код:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let count = fetchedRC.fetchedObjects?.count ?? 0
    return count
}


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

    items.append(fetchedRC.object(at: indexPath))

    if items[indexPath.row].isComplete == true {
        let attributeString = NSMutableAttributedString(string: items[indexPath.row].name!)

        attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeString.length))
        cell.nameL.attributedText = attributeString

    } else {
        cell.nameL.text = items[indexPath.row].name
    }

    return cell
}

1 Ответ

0 голосов
/ 07 июня 2019

Вы используете NSFetchedResultsController, поэтому не стоит поддерживать отдельный массив (ваш items); Просто используйте метод object(at:), чтобы получить предмет при необходимости.

У вас также есть проблема, так как вы добавляете элемент в массив items в cellForRowAt: - cellForRowAt: будет вызываться более одного раза для данного indexPath, и нет гарантии, в каком порядке cellForRowAt: вызывается (например, при прокрутке таблицы).

Если вы используете NSFetchedResultsController, вам действительно следует реализовать его делегат и использовать методы делегата для обновления таблицы.

Избавьтесь от массива items и используйте что-то вроде:

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

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        let newIndexPath: IndexPath? = newIndexPath != nil ? IndexPath(row: newIndexPath!.row, section: 0) : nil
        let currentIndexPath: IndexPath? = indexPath != nil ? IndexPath(row: indexPath!.row, section: 0) : nil

        switch type {
        case .insert:
            self.tableView.insertRows(at: [newIndexPath!], with: .automatic)

        case .delete:
            self.tableView.deleteRows(at: [currentIndexPath!], with: .fade)

        case .move:
            self.tableView.moveRow(at: currentIndexPath!, to: newIndexPath!)

        case .update:
            self.tableView.reloadRows(at: [currentIndexPath!], with: .automatic)
        }
    }

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

Ваш cellForRowAt должен быть:

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

    let item = fetchedRC.object(at:indexPath)

    if item.isComplete == true {
        let attributeString = NSMutableAttributedString(string: item.name ?? "")

        attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeString.length))
        cell.nameL.attributedText = attributeString

    } else {
        cell.nameL.text = item.name
    }

    return cell
}

и ваш код удаления должен быть:

if editingStyle == .delete {
    context.delete(fetchedRC.object(at:indexPath))

    do {
        try context.save()
    } catch let error as NSError {
        print("Could not save. \(error.localizedDescription)")
    }
}

Не забудьте назначить self на fetchedRC.delegate при создании контроллера выбранных результатов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...