段中的行数不在TableView中更新

问题描述:

当我在UITableView中有一个项目时,我遇到了困难,我更新了确定该段的属性值。段中的行数不在TableView中更新

我已经知道它与didChangeSection和选项NSFetchedResultsChangeType.Update有关。但就我所能得到的而言。

我不知道我需要在那里放置哪些代码来更新节中的行数。

这是确切的错误代码,我得到:

2016-04-17 20:00:37.126 EZ List[13722:1469523] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/UITableView.m:1716 2016-04-17 20:00:37.126 EZ List[13722:1469523] CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. 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 (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)

而且这是在我的视图控制器代码:

import UIKit 
import CoreData 

class ListItemsTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { 

let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext 
var frc: NSFetchedResultsController = NSFetchedResultsController() 
var list: Lists? 
var catalog: Catalog? 

override func viewDidLoad() { 
    super.viewDidLoad() 

    self.tableView.reloadData() 
    self.title = list?.name 

    frc = getFCR() 
    frc.delegate = self 

    do { 
     try frc.performFetch() 
    } catch { 
     print("Failed to perform inital fetch") 
    } 

    // 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 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 viewDidAppear(animated: Bool) { 

    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, numberOfRowsInSection section: Int) -> Int { 

    if let sections = frc.sections { 
     let currentSection = sections[section] 
     return currentSection.numberOfObjects 
    } 

    return 0 

} 

override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 

    if let sections = frc.sections { 
     let currentSection = sections[section] 
     return currentSection.name 
    } 

    return nil 

} 

override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { 

    let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView 
    header.contentView.backgroundColor = UIColor(red: 84/255, green: 200/255, blue: 214/255, alpha: 0.5) 
    header.textLabel!.textColor = UIColor.whiteColor() 
    //header.alpha = 0.5 //make the header transparent 

} 

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

    let cell = tableView.dequeueReusableCellWithIdentifier("listContentCell", forIndexPath: indexPath) as! ListItemsTableViewCell 
    let item = frc.objectAtIndexPath(indexPath) as! Items 

    cell.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) 
    cell.itemName.text = item.name 
    cell.itemSection.text = item.section 
    cell.itemQty.text = "Qty: \(item.qty!)" 
    cell.itemSize.text = item.size 
    cell.itemPrice.text = floatToCurrency(Float(item.cost!)) 
    cell.itemImage.image = UIImage(data: item.image!) 
    cell.itemID.text = String(item.id!) 

    return cell 

} 

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { 

    cell.backgroundColor = UIColor.clearColor() 

} 

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 

     // Code to come 

    } 

    let qty = UITableViewRowAction(style: .Normal, title: "Qty") { (action, indexPath) in 

     // Code to come 

    } 

    edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) 

    return [delete, edit, qty] 

} 

func controllerWillChangeContent(controller: NSFetchedResultsController) { 

    tableView.beginUpdates() 

} 

func controllerDidChangeContent(controller: NSFetchedResultsController) { 

    tableView.endUpdates() 

} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 

    switch type { 
    case NSFetchedResultsChangeType.Delete: 
     self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Insert: 
     self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Update: 
     let cell = self.tableView.cellForRowAtIndexPath(indexPath!) as! ListItemsTableViewCell 
     let item = self.frc.objectAtIndexPath(indexPath!) as! Items 

     cell.itemName.text = item.name 
     cell.itemSection.text = item.section 
     cell.itemQty.text = "Qty: \(item.qty!)" 
     cell.itemSize.text = item.size 
     cell.itemPrice.text = floatToCurrency(Float(item.cost!)) 
     cell.itemImage.image = UIImage(data: item.image!) 
     cell.itemID.text = String(item.id!) 
    default: 
     print("didChangeObject Default was accessed") 
     break 
    } 

} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 

    switch type { 
    case NSFetchedResultsChangeType.Delete: 
     self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Insert: 
     self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Update: 
     self.tableView.reloadData() 
    default: 
     print("didChangeSection Default was accessed") 
     break 
    } 

} 

func fetchRequest() -> NSFetchRequest { 

    let fetchRequest = NSFetchRequest(entityName: "Items") 
    let sortDesc1 = NSSortDescriptor(key: "section", ascending: true) 
    let sortDesc2 = NSSortDescriptor(key: "isChecked", ascending: true) 
    let sortDesc3 = NSSortDescriptor(key: "name", ascending: true) 
    fetchRequest.sortDescriptors = [sortDesc1, sortDesc2, sortDesc3] 

    return fetchRequest 

} 

func getFCR() -> NSFetchedResultsController { 

    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "section" , cacheName: nil) 

    return frc 

} 

func getCatalog(id: NSNumber) -> Catalog { 

    var cat: Catalog? 

    let fetchReq = NSFetchRequest(entityName: "Catalog") 
    let pred = NSPredicate(format: "%K == %@", "id", id) 
    fetchReq.predicate = pred 

    do { 
     let check = try moc.executeFetchRequest(fetchReq) 
     cat = (check.first as! Catalog) 
    } catch { 
     fatalError("Failed fetching Catalog Entry matching Item") 
    } 

    return cat! 
} 

func floatToCurrency(flt: Float) -> String { 

    let formatter = NSNumberFormatter() 
    formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle 
    return String(formatter.stringFromNumber(flt)!) 

} 

/* 
// 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?) { 

    var id: NSNumber 

    if (segue.identifier == "listItemView") { 
     let cell = sender as! UITableViewCell 
     let indexPath = self.tableView.indexPathForCell(cell) 

     let itemCont: ViewItemViewController = segue.destinationViewController as! ViewItemViewController 
     let item: Items = self.frc.objectAtIndexPath(indexPath!) as! Items 
     itemCont.item = item 
     id = item.id! 
     itemCont.catalog = getCatalog(id) 
    } else if (segue.identifier == "listItemViewEdit") { 
     let cell = sender as! UITableViewCell 
     let indexPath = self.tableView.indexPathForCell(cell) 

     let itemCont: AddItemListViewController = segue.destinationViewController as! AddItemListViewController 
     let item: Items = self.frc.objectAtIndexPath(indexPath!) as! Items 
     itemCont.item = item 
     id = item.id! 
     itemCont.catalog = getCatalog(id) 
     itemCont.list = list 
    } 

} 

} 

我觉得我真的很接近得到它对,但我只需要额外的推动。

更新重装行因此,原来它不符合didChangeSection和NSFetchedResultsChangeType.Update问题。这是didChangeObject和NSFetchedResultsChangeType.Move的问题。

以下代码解决了我的问题。其他的都是一样的,所以我只是把这两个功能。

添加NSFetchedResultsChangeType.Move并在它内部删除旧位置和新位置。

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 

    switch type { 
    case NSFetchedResultsChangeType.Delete: 
     self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Insert: 
     self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) 
    /*case NSFetchedResultsChangeType.Update: 
     tableView.reloadSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)*/ 
    default: 
     print("didChangeSection Default was accessed") 
     break 
    } 

} 

移动NSFetchedResultsChangeType.Update,因为该移动负责节更新。

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 

    switch type { 
    case NSFetchedResultsChangeType.Delete: 
     self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Insert: 
     self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    case NSFetchedResultsChangeType.Update: 
     let cell = self.tableView.cellForRowAtIndexPath(indexPath!) as! ListItemsTableViewCell 
     let item = self.frc.objectAtIndexPath(indexPath!) as! Items 

     cell.itemName.text = item.name 
     cell.itemSection.text = item.section 
     cell.itemQty.text = "Qty: \(item.qty!)" 
     cell.itemSize.text = item.size 
     cell.itemPrice.text = floatToCurrency(Float(item.cost!)) 
     cell.itemImage.image = UIImage(data: item.image!) 
     cell.itemID.text = String(item.id!) 
    case NSFetchedResultsChangeType.Move: 
     self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
     self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    } 

} 

希望这可以帮助别人!

尝试在你所谈论的,并检查这是否有助于 https://forums.developer.apple.com/thread/12184

+0

所以你认为它不是我的代码问题,而是Xcode的问题? –

+0

也许是的,这就是线程所说的,尝试使用断点检查委托方法触发多少次,并在更新时插入或删除执行了哪个NSFetchedResultsChangeType。首先尝试重新加载行。 –