删除Core Data

时间:2016-03-10 16:56:01

标签: ios swift uitableview

我首先要说的是我在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.
}

2 个答案:

答案 0 :(得分:4)

你需要打电话......

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

...当您的数据源发生变化时。当你调用上面的方法时,它会询问你的tableView代表该部分中有多少行,并且该方法仍然返回2。一旦知道该方法返回1,就必须调用上述方法。

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

查看NSFetchedResultsControllerDelegateReference)的参考资料。它详细说明了如何处理与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
    }

}

}