我首先要说的是我在Xcode / Swift制作应用程序时非常陌生。我从未真正用Obj-C做过任何事情,所以Swift真的是我第一次进入iOS编码。
我的问题是我在删除核心数据记录后尝试删除表格单元格。我似乎能够很好地删除Core数据,但每次我尝试删除单元格行时都会得到相同的错误。
我得到的错误如下:
2016-03-10 08:49:56.484 EZ列表[31764:3225607] *断言失败 - [UITableView _endCellAnimationsWithContext:],/ BuildRoot / Library / Cache / com.apple.xbs / Source / UIKit_Sim /UIKit-3512.30.14/UITableView.m:1720 2016-03-10 08:49:56.487 EZ列表[31764:3225607] * 由于未捕获的异常终止应用程序' 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]
}
经过测试后,我已经确认问题出在
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
但我不明白为什么它认为部分中的行无效。任何帮助将非常感谢。到目前为止,我所有的搜索都提出了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
}
EDIT2: 这是我的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.
}
答案 0 :(得分:4)
你需要打电话......
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
...当您的数据源发生变化时。当你调用上面的方法时,它会询问你的tableView代表该部分中有多少行,并且该方法仍然返回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
引用也非常有用(Reference)。
答案 1 :(得分:0)
我知道了!但这一切都归功于马克,他告诉我我需要做什么。在谷歌搜索了一些东西后,我能够迅速找到相应的东西。我只是想发布我的最终代码,所以其他需要最终结果的人也有这个! 再次感谢Mark,我花了一个星期的时间来转动这一件事。
我移动了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
}
}
}