核心数据删除logging后,不能删除索引处的表格行

我会开始说,我是非常新的Xcode / Swift应用程序。 我从来没有用Obj-C做过任何事情,所以Swift是我第一次进入iOS编程。

我的问题是,我正试图删除核心数据logging后删除表格单元格。 我似乎能够删除核心数据就好,但每次我尝试删除单元格行时得到相同的错误。

我得到的错误如下所示:

2016-03-10 08:49:56.484 EZ列表[31764:3225607] *声明失败 – [UITableView _endCellAnimationsWithContext:],/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.30.14 /UITableView.m:1720 2016-03-10 08:49:56.487 EZ List [31764:3225607] *终止应用程序,由于未捕获的exception'NSInternalInconsistencyException',原因:'无效的更新: (2)之后的现有节中包含的行的数量必须等于更新前(2)之前该节中包含的行数加上或减去从该节插入或删除的行数(0插入,1删除),加上或减去移入或移出该部分的行数(移入0,移出0)。 [… 等等]

而我用来删除的代码是这样的:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in // delete item at indexPath let request = self.fetchRequest() var fetchResults = [AnyObject]() do { fetchResults = try self.moc.executeFetchRequest(request) } catch { fatalError("Fetching Data to Delete Failed") } self.moc.deleteObject(fetchResults[indexPath.row] as! NSManagedObject) fetchResults.removeAtIndex(indexPath.row) do { try self.moc.save() } catch { fatalError("Failed to Save after Delete") } self.lists.removeAtIndex(indexPath.row) self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) } let edit = UITableViewRowAction(style: .Normal, title: "Edit") { (action, indexPath) in // edit item at indexPath } edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) return [delete, edit] } 

经过testing一堆,我已经证实,问题是

 self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 

但我不明白为什么它认为部分行是无效的。 任何帮助将不胜感激。 到目前为止,我所有的search都已经提出了Obj-C的答案或迅速的答案,似乎并没有解决我的问题。

编辑:添加numberOfRowsInSection的代码

 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows if let sections = frc.sections { let currentSection = sections[section] return currentSection.numberOfObjects } return 0 } 

编辑2:这是我的ViewController的完整代码**请记住,我一直在尝试一些不同的东西,让它的工作,所以有几个注释掉那里的东西。

 import UIKit import CoreData class ListTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext var frc: NSFetchedResultsController = NSFetchedResultsController() var lists : [Lists] = [] /* var listNames: [String] = ["Any List", "Other List"] var listItemCount: [Int] = [5, 6] var listIcons: [UIImage] = [UIImage(named: "Generic List")!, UIImage(named: "Generic List")!] */ override func viewWillAppear(animated: Bool) { let imageView = UIImageView(image: UIImage(named: "TableBackground")) imageView.contentMode = .ScaleAspectFill self.tableView.backgroundView = imageView self.tableView.tableFooterView = UIView(frame: CGRectZero) do { self.lists = try moc.executeFetchRequest(fetchRequest()) as! [Lists] } catch { fatalError("Failed setting lists array to fetch request") } self.tableView.reloadData() } override func viewDidLoad() { super.viewDidLoad() checkAnyList() frc = getFCR() frc.delegate = self do { try frc.performFetch() } catch { fatalError("Failed to perform inital fetch") } self.tableView.reloadData() // Uncomment the following line to preserve selection between presentations //self.clearsSelectionOnViewWillAppear = true // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem() } override func viewDidAppear(animated: Bool) { checkAnyList() frc = getFCR() frc.delegate = self do { try frc.performFetch() } catch { fatalError("Failed to perform inital fetch") } self.tableView.reloadData() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Table view data source override func numberOfSectionsInTableView(tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections if let sections = frc.sections { return sections.count } return 0 } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { cell.backgroundColor = UIColor.clearColor() } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows if let sections = frc.sections { let currentSection = sections[section] return currentSection.numberOfObjects } return 0 } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let listCellReturn = tableView.dequeueReusableCellWithIdentifier("listCell", forIndexPath: indexPath) as! ListTableViewCell //let list = frc.objectAtIndexPath(indexPath) as! Lists let list = self.lists[indexPath.row] listCellReturn.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) listCellReturn.listNameLabel.text = list.name listCellReturn.listIconImage.image = UIImage(data: (list.image)!) /* listCellReturn.listNameLabel.text = listNames[indexPath.row] listCellReturn.itemCountLabel.text = "Items: \(listItemCount[indexPath.row])" listCellReturn.listIconImage.image = listIcons[indexPath.row] */ return listCellReturn } override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in // delete item at indexPath let request = self.fetchRequest() var fetchResults = [AnyObject]() do { fetchResults = try self.moc.executeFetchRequest(request) } catch { fatalError("Fetching Data to Delete Failed") } self.moc.deleteObject(fetchResults[indexPath.row] as! NSManagedObject) fetchResults.removeAtIndex(indexPath.row) do { try self.moc.save() } catch { fatalError("Failed to Save after Delete") } self.lists.removeAtIndex(indexPath.row) self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) } let edit = UITableViewRowAction(style: .Normal, title: "Edit") { (action, indexPath) in // edit item at indexPath } edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) return [delete, edit] } func fetchRequest() -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Lists") let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) fetchRequest.sortDescriptors = [sortDescriptor] return fetchRequest } func getFCR() -> NSFetchedResultsController { frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil) return frc } func checkAnyList() { var exists: Bool = false let fetchReq = NSFetchRequest(entityName: "Lists") let pred = NSPredicate(format: "%K == %@", "name", "Any List") fetchReq.predicate = pred do { let check = try moc.executeFetchRequest(fetchReq) for rec in check { if let name = rec.valueForKey("name") { if (name as! String == "Any List") { exists = true } } } if (exists == false) { let entityDesc = NSEntityDescription.entityForName("Lists", inManagedObjectContext: moc) let list = Lists(entity: entityDesc!, insertIntoManagedObjectContext: moc) list.name = "Any List" list.image = UIImagePNGRepresentation(UIImage(named: "Any List")!) do { try moc.save() } catch { fatalError("New list save failed") } } } catch { fatalError("Error checking for Any List") } //let check = try moc.executeFetchRequest(fetchReq) //for rec in check { //} catch { } /* // Override to support conditional editing of the table view. override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { // Return false if you do not want the specified item to be editable. return true } */ /* // Override to support editing the table view. override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == .Delete { // Delete the row from the data source tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) } else if editingStyle == .Insert { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } */ /* // Override to support rearranging the table view. override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { } */ /* // Override to support conditional rearranging of the table view. override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool { // Return false if you do not want the item to be re-orderable. return true } */ // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // Get the new view controller using segue.destinationViewController. // Pass the selected object to the new view controller. } 

你需要打电话给…

 self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 

…当你的数据源被改变了。 当你调用上面的方法时,它会询问你的tableView的委托该节中有多less行,并且该方法仍然返回2 。 一旦知道该方法返回1 ,您必须调用上述方法。

编辑:获取更多信息后更新。

看一下NSFetchedResultsControllerDelegate ( Reference )的参考资料 。 它详细介绍了如何处理UITableView数据的更改。 基本上从你的编辑方法中删除deleteRowsAtIndexPaths ,而是在相应的委托方法中执行tableView更新。 一些沿着这些线应该帮助你(从上面的参考):

 - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } 

你可能不需要处理每个案件。 NSFetchedResultsController引用也很有帮助( 参考 )。

我知道了!!! 但是,这一切都要感谢马克,他告诉我我需要做什么。 在Google上search了一些东西之后,我能够迅速find相应的东西。 我只是想发布我的最终代码,以便任何需要最终结果的人也能拥有它! 马克再次感谢你,我花了一个星期在这一件事情上旋转我的轮子。

我移动了self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)

这里是全视图控制器代码

 import UIKit import CoreData class ListTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext var frc: NSFetchedResultsController = NSFetchedResultsController() override func viewWillAppear(animated: Bool) { let imageView = UIImageView(image: UIImage(named: "TableBackground")) imageView.contentMode = .ScaleAspectFill self.tableView.backgroundView = imageView self.tableView.tableFooterView = UIView(frame: CGRectZero) } override func viewDidLoad() { super.viewDidLoad() checkAnyList() frc = getFCR() frc.delegate = self do { try frc.performFetch() } catch { fatalError("Failed to perform inital fetch") } // Uncomment the following line to preserve selection between presentations //self.clearsSelectionOnViewWillAppear = true } override func viewDidAppear(animated: Bool) { checkAnyList() frc = getFCR() frc.delegate = self do { try frc.performFetch() } catch { fatalError("Failed to perform inital fetch") } self.tableView.reloadData() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Table view data source override func numberOfSectionsInTableView(tableView: UITableView) -> Int { if let sections = frc.sections { return sections.count } return 0 } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { cell.backgroundColor = UIColor.clearColor() } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let sections = frc.sections { let currentSection = sections[section] return currentSection.numberOfObjects } return 0 } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("listCell", forIndexPath: indexPath) as! ListTableViewCell let list = frc.objectAtIndexPath(indexPath) as! Lists cell.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) cell.listNameLabel.text = list.name cell.listIconImage.image = UIImage(data: (list.image)!) //listCellReturn.itemCountLabel.text = "Items: \(listItemCount[indexPath.row])" return cell } override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in let request = self.fetchRequest() var fetchResults = [AnyObject]() do { fetchResults = try self.moc.executeFetchRequest(request) } catch { fatalError("Fetching Data to Delete Failed") } self.moc.deleteObject(fetchResults[indexPath.row] as! NSManagedObject) fetchResults.removeAtIndex(indexPath.row) do { try self.moc.save() } catch { fatalError("Failed to Save after Delete") } } let edit = UITableViewRowAction(style: .Normal, title: "Edit") { (action, indexPath) in self.performSegueWithIdentifier("newList", sender: tableView.cellForRowAtIndexPath(indexPath)) } edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) return [delete, edit] } internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { if (type == NSFetchedResultsChangeType.Delete) { self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) } } func fetchRequest() -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Lists") let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) fetchRequest.sortDescriptors = [sortDescriptor] return fetchRequest } func getFCR() -> NSFetchedResultsController { frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil) return frc } func checkAnyList() { var exists: Bool = false let fetchReq = NSFetchRequest(entityName: "Lists") let pred = NSPredicate(format: "%K == %@", "name", "Any List") fetchReq.predicate = pred do { let check = try moc.executeFetchRequest(fetchReq) for rec in check { if let name = rec.valueForKey("name") { if (name as! String == "Any List") { exists = true } } } if (exists == false) { let entityDesc = NSEntityDescription.entityForName("Lists", inManagedObjectContext: moc) let list = Lists(entity: entityDesc!, insertIntoManagedObjectContext: moc) list.name = "Any List" list.image = UIImagePNGRepresentation(UIImage(named: "Any List")!) do { try moc.save() } catch { fatalError("New list save failed") } } } catch { fatalError("Error checking for Any List") } } /* // Override to support conditional editing of the table view. override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { // Return false if you do not want the specified item to be editable. return true } */ /* // Override to support editing the table view. override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == .Delete { // Delete the row from the data source tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) } else if editingStyle == .Insert { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } */ /* // Override to support rearranging the table view. override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { } */ /* // Override to support conditional rearranging of the table view. override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool { // Return false if you do not want the item to be re-orderable. return true } */ // MARK: - Navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if let _ = sender as? UITableViewCell { let cell = sender as! UITableViewCell let indexPath = tableView.indexPathForCell(cell) let itemCont : AddListViewController = segue.destinationViewController as! AddListViewController let list : Lists = self.frc.objectAtIndexPath(indexPath!) as! Lists itemCont.list = list } } 

}