删除Core Data中的记录后无法删除索引处的表行 [英] Can't delete table row at index after delete record in Core Data

查看:23
本文介绍了删除Core Data中的记录后无法删除索引处的表行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我首先要说我对在 Xcode/Swift 中制作应用程序非常陌生.我从来没有用 Obj-C 做过任何事情,所以 Swift 真的是我第一次接触 iOS 编码.

I'll start out by saying that I am VERY new to making apps in Xcode / Swift. I've never really done anything with Obj-C so Swift is really my first entry into coding for iOS.

我的问题是我试图在删除核心数据记录后删除表格单元格.我似乎可以很好地删除核心数据,但是每次尝试删除单元格行时都会出现相同的错误.

My problem is that I am attempting to delete the table cell after deleting the Core Data record. I seem to be able to delete the Core data just fine, but get the same error every time I try to delete the cell row.

我得到的错误如下:

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

2016-03-10 08:49:56.484 EZ List[31764:3225607] * Assertion failure in -[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] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' [... And so on]

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

And the code I'm using to delete is this:

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]

}

经过大量测试后,我确认问题出在

After testing a bunch I have verified that the problem is with

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

但我不明白为什么它认为部分中的行无效.任何帮助将不胜感激.到目前为止,我的所有搜索都提供了 Obj-C 答案或似乎无法解决我的问题的快速答案.

But I don't understand why it thinks the rows in sections are invalid. Any help would much appreciated. So far all my searching has come up with Obj-C answers or swift answers that don't seem to fix my problem.

为 numberOfRowsInSection 添加代码

Adding code for 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
}

这是我的 ViewController 的完整代码**请记住,我一直在尝试一些不同的方法来使其正常工作,因此其中有一些已注释掉的内容.

Here is the full code for my ViewController **Keep in mind that I've been trying a few different things to get it to work so there are a few commented out things in there.

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 的委托在该部分中有多少行,并且该方法仍然返回 2.一旦知道该方法返回 1,您必须调用上述方法.

...when your data source has been changed. When you call the above method, it asks your tableView's delegate how many rows in that section, and that method is still returning 2. You must call the above method once that method is known to return 1.

获取更多信息后更新.

查看 NSFetchedResultsControllerDelegate 的参考资料(参考).它详细说明了您应该如何处理与 UITableView 相关的数据更改.基本上从您的编辑方法中删除 deleteRowsAtIndexPaths,而是在适当的委托方法中执行 tableView 更新.这些方面的内容应该可以帮助您(取自上述参考资料):

Take a look at the reference material for NSFetchedResultsControllerDelegate (Reference). It details how you should handle changes in data with respect to a UITableView. Basically remove the deleteRowsAtIndexPaths from your edit method, and instead perform the tableView update in the appropriate delegate method. Something along these lines should help you (taken from the above reference):

- (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 参考也很有帮助(参考).

You probably don't need to handle each case. The NSFetchedResultsController reference is also quite helpful (Reference).

这篇关于删除Core Data中的记录后无法删除索引处的表行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆