Я пишу свое приложение на Swift 3 (преобразованное) в Xcode 8.
NSFetchedResultsController вызывает у меня серьезную ошибку приложения.
Мое основное табличное представление разделено текстовым идентификатором под названием «yearText», который устанавливается для любой данной записи события (NSManagedObject), когда пользователь изменяет «Дату события» с помощью средства выбора даты. Когда средство выбора изменяется или закрывается, год удаляется из даты, преобразуется в текст и сохраняется в объекте Event. Контекст управляемого объекта затем сохраняется.
Если выбрана дата, для которой уже существует раздел (например, год «2020»), выдается ошибка:
[ошибка] ошибка: Серьезная ошибка приложения. Исключение было перехвачено делегатом NSFetchedResultsController во время вызова -controllerDidChangeContent:. Недопустимое обновление: недопустимое количество строк в разделе 0. Количество строк, содержащихся в существующем разделе после обновления (2), должно быть равно количеству строк, содержащихся в этом разделе до обновления (1), плюс или минус число строк, вставленных или удаленных из этого раздела (0 вставленных, 0 удаленных) и плюс или минус количество строк, перемещенных в этот раздел или из него (0 добавленных, 0 удаленных). с информацией о пользователе (нулевой)
Пока выбранная дата не находится в пределах года, в котором уже есть раздел, названный в ее честь, все работает нормально.
Вот мой соответствующий код для обновления базы данных и таблицы:
var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
// Fetch the default object (Event)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
let entity = NSEntityDescription.entity(forEntityName: "Event", in: managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 60
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: "yearText", cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// Implement error handling code here.
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>?
// MARK: - UITableViewDelegate
extension EventListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! EventCell
cell.isSelected = true
configureCell(withCell: cell, atIndexPath: indexPath)
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! EventCell
cell.isSelected = false
configureCell(withCell: cell, atIndexPath: indexPath)
}
}
// MARK: - UITableViewDataSource
extension EventListViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EventCell", for: indexPath) as! EventCell
configureCell(withCell: cell, atIndexPath: indexPath)
return cell
}
func configureCell(withCell cell: EventCell, atIndexPath indexPath: IndexPath) {
// bunch of stuff to make the cell pretty and display the data
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let context = fetchedResultsController.managedObjectContext
context.delete(fetchedResultsController.object(at: indexPath) as! NSManagedObject)
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//print("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.name
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
// make the section header look good
view.tintColor = kWPPTintColor
let header = view as! UITableViewHeaderFooterView
header.textLabel?.textColor = kWPPDarkColor
header.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.subheadline)
}
}
// MARK: - NSFetchedResultsControllerDelegate
extension EventListViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
default:
return
}
}
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:
configureCell(withCell: tableView.cellForRow(at: indexPath!)! as! EventCell, atIndexPath: indexPath!)
case .move:
tableView.moveRow(at: indexPath!, to: newIndexPath!)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
}
Я надеюсь, что вы можете предложить мне несколько предложений. Спасибо.
РЕДАКТИРОВАТЬ: вынул некоторый код, который только мешал, и пересмотрел .move для использования .moveRow
РЕДАКТИРОВАТЬ 2: Добавлен код генерации FRC.