Swift NSFetchedResultsController在节索引更改时崩溃
我正在用Xcode 8的Swift 3(已转换)编写我的应用程序 NSFetchedResultsController给我造成了严重的应用程序错误 我的主表视图由一个名为“yearText”的文本标识符分割,当用户使用日期选择器更改“事件日期”时,该标识符将在任何给定的事件记录(NSManagedObject)上设置。更改或取消选取器时,年份将从日期中删除,转换为文本,并存储在事件对象中。然后保存托管对象上下文 如果选择的日期已经存在一个节(即“2020年”),则抛出一个错误,说明: [错误]错误:严重的应用程序错误。在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托捕获到异常。无效更新:节0中的行数无效。更新(2)后现有节中包含的行数必须等于更新(1)前该节中包含的行数,加上或减去从该节中插入或删除的行数(0插入,0删除),加上或减去移入或移出该节的行数(0移入,0移出). 带userInfo(空) 只要选择的日期不在一年之内,并且已经有一个以它命名的部分,它就可以正常工作 以下是我更新数据库和tableview的相关代码:Swift NSFetchedResultsController在节索引更改时崩溃,swift,uitableview,core-data,nsfetchedresultscontroller,sections,Swift,Uitableview,Core Data,Nsfetchedresultscontroller,Sections,我正在用Xcode 8的Swift 3(已转换)编写我的应用程序 NSFetchedResultsController给我造成了严重的应用程序错误 我的主表视图由一个名为“yearText”的文本标识符分割,当用户使用日期选择器更改“事件日期”时,该标识符将在任何给定的事件记录(NSManagedObject)上设置。更改或取消选取器时,年份将从日期中删除,转换为文本,并存储在事件对象中。然后保存托管对象上下文 如果选择的日期已经存在一个节(即“2020年”),则抛出一个错误,说明: [错误]错
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()
}
}
var fetchedResultsController:NSFetchedResultsController{
如果_fetchedResultsController!=nil{
return\u fetchedResultsController!
}
//获取默认对象(事件)
让fetchRequest=NSFetchRequest()
让entity=NSEntityDescription.entity(在:managedObjectContext!中,名为“事件”)
fetchRequest.entity=实体
//将批次大小设置为合适的数字。
fetchRequest.fetchBatchSize=60
//根据需要编辑排序键。
让sortDescriptor=NSSortDescriptor(键:“日期”,升序:false)
fetchRequest.sortDescriptors=[sortDescriptor]
//如果合适,请编辑节名称密钥路径和缓存名称。
//对于节名称键路径,nil表示“无节”。
设aFetchedResultsController=NSFetchedResultsController(fetchRequest:fetchRequest,managedObjectContext:managedObjectContext!,sectionNameKeyPath:“yearText”,cacheName:nil)
aFetchedResultsController.delegate=self
_fetchedResultsController=aFetchedResultsController
做{
请尝试_fetchedResultsController!.performFetch()
}抓住{
//在这里实现错误处理代码。
中止
}
return\u fetchedResultsController!
}
var_fetchedResultsController:NSFetchedResultsController?
//标记:-UITableViewDelegate
扩展事件列表视图控制器:UITableViewDelegate{
func tableView(tableView:UITableView,didSelectRowAt indexPath:indexPath){
将cell=tableView.cellForRow(at:indexPath)设为!EventCell
cell.isSelected=true
configureCell(withCell:cell,atIndexPath:indexPath)
}
func tableView(tableView:UITableView,在indexPath:indexPath中取消行){
将cell=tableView.cellForRow(at:indexPath)设为!EventCell
cell.isSelected=false
configureCell(withCell:cell,atIndexPath:indexPath)
}
}
//标记:-UITableViewDataSource
扩展事件列表视图控制器:UITableViewDataSource{
func numberOfSections(在tableView:UITableView中)->Int{
返回fetchedResultsController.sections?.count±0
}
func tableView(tableView:UITableView,numberofrowsinssection:Int)->Int{
让sectionInfo=fetchedResultsController.sections![section]
返回sectionInfo.numberOfObjects
}
func tableView(tableView:UITableView,cellForRowAt indexath:indexPath)->UITableViewCell{
让cell=tableView.dequeueReusableCell(标识符为:“EventCell”,for:indexPath)作为!EventCell
configureCell(withCell:cell,atIndexPath:indexPath)
返回单元
}
func configureCell(withCell单元格:EventCell,atIndexPath indexPath:indexPath){
//一堆东西,使细胞漂亮,并显示数据
}
func tableView(tableView:UITableView,canEditRowAt indexath:indexPath)->Bool{
//如果不希望指定的项可编辑,则返回false。
返回真值
}
func tableView(tableView:UITableView,commit editingStyle:UITableViewCellEditingStyle,forRowAt indexPath:indexPath){
如果editingStyle==.delete{
let context=fetchedResultsController.managedObjectContext
context.delete(fetchedResultsController.object(位于:indexPath)为!NSManagedObject)
做{
尝试context.save()
}抓住{
//将此实现替换为适当处理错误的代码。
//abort()导致应用程序生成崩溃日志并终止。您不应该在装运应用程序中使用此函数,尽管它在开发过程中可能很有用。
//打印(“未解决的错误\(error),\(error.userInfo)”)
中止
}
}
}
func tableView(tableView:UITableView,titleForHeaderInSection:Int)->String{
让sectionInfo=fetchedResultsController.sections![section]
返回sectionInfo.name
}
func tableView(tableView:UITableView,willDisplayHeaderView:UIView,for section section:Int){
//使节标题看起来很好
view.tintColor=kWPPTintColor
let header=查看为!UITableViewHeaderFooterView
header.textLabel?.textColor=KWPDARKColor
header.textlab?.font=UIFont.preferredFont(forTextStyle:uifontextstyle.subheadline)
}
}
//标记:-NSFetchedResultsControllerDelegate
扩展事件列表视图控制器:NSFetchedResultsControllerDelegate{
func controllerWillChangeContent(\控制器:NSFetchedRes)
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
self.tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
self.tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
self.tableView.reloadRows(at: [indexPath!], with: .fade)
case .move:
self.tableView.insertRows(at: [newIndexPath!], with: .fade)
self.tableView.deleteRows(at: [indexPath!], with: .fade)
}
}