部分中的行计数未在 TableView 中更新
Posted
技术标签:
【中文标题】部分中的行计数未在 TableView 中更新【英文标题】:Rows in Sections count not updating in TableView 【发布时间】:2016-04-18 03:17:39 【问题描述】:当我的 UITableView 中有一个项目并且我更新确定该部分的属性值时,我遇到了困难。
我发现它与 didChangeSection 和 NSFetchedResultsChangeType.Update 选项有关。但这是我能做到的。
我不确定我需要在其中放入什么代码来更新节中的行数。
这是我得到的确切错误代码:
2016-04-17 20:00:37.126 EZ List[13722:1469523] *** 断言失败 -[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:错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间,从 NSFetchedResultsController 的委托中捕获了异常。无效更新:第 0 节中的行数无效。更新后现有节中包含的行数 (2) 必须等于更新前该节中包含的行数 (3),加上或减去数字从该部分插入或删除的行数(0 插入,0 删除)加上或减去移入或移出该部分的行数(0 移入,0 移出)。与 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
我觉得我真的很接近正确,但我只是需要额外的推动。
【问题讨论】:
【参考方案1】:尝试重新加载您正在谈论的更新中的行并检查是否有帮助 https://forums.developer.apple.com/thread/12184
【讨论】:
所以你认为这不是我的代码的问题,而是 Xcode 的问题? 也许是的,这就是线程所说的,尝试使用断点检查委托方法触发的次数以及更新、插入或删除哪个 NSFetchedResultsChangeType 执行。首先尝试重新加载行。跨度> 【参考方案2】:事实证明,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)
希望这对其他人有帮助!
【讨论】:
以上是关于部分中的行计数未在 TableView 中更新的主要内容,如果未能解决你的问题,请参考以下文章
当每个部分的行数不同时,如何获取 numberOfRows 的计数?
tableView.reloadRows(at:[indexPath],带有:.none)会像这样崩溃吗?