CoreData:错误:严重的应用程序错误。在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托中捕获到异常。尝试在使用userInfo(null)
更新之前从第0部分删除第1行,该第1行只包含1行在我的应用中 - 我有一个表视图。表视图有两个部分。该部分是使用核心数据实体中的瞬态属性来确定的,如下所示 -
@objc var section: String?{
return event < Date() ? "before" : "after"
}
该应用应该像这样工作:
表格视图控制器允许使用单元格滑动删除/取消。当从触发委托方法的fetchedResults
控制器中删除某个项目时,我会删除 performBatchUpdates 中的项目。这适用于插入和删除。
当用户选择一行时,它会转移到集合视图。视图在collectionView
中滚动到所选项目(项目跨越整个视图)。用户可以在各种单元项之间来回滚动。可以从集合视图中删除项目。我也在这里使用 performBatchUpdates 来删除委托方法中的项目。这也很好。
问题发生在我从集合视图转换回表View后,只有当我碰巧删除集合View中的项目并返回tableView时才会出现问题。
a)如果我插入项目 - 没问题。
b)如果我从与我在集合视图中删除项的部分相同的部分删除行。它可以正常工作。
c)上述错误仅在我尝试从其他部分(第0或第1部分)删除某个项目时显示,即我在collectionView
中未删除的部分。
**第0节**
项目A
项目B
第1节
项目C. 项目D
如果我在收藏视图中删除项目A ,然后返回到表格视图,我会看到以下内容(这是正确的)
第0节
项目B
第1节
项目C. 项目D
但是当我尝试删除项目D或项目C.控制器认为我试图删除第0部分中的项目!
但如果我在第0部分中首先删除了项目B,则没有问题。
如果在删除第0部分中的项目B后删除第1部分中的项目C和D - 没有问题。
我尝试通过在从核心数据(获取结果控制器)删除时打印indexPaths
,并在触发时在didChangeObject
,didChangeSection
等中打印。它会打印我正在尝试删除的正确行和部分。我正在努力解决这个问题。有没有人遇到类似问题并有解决方案?
我的tableViewController
var deleteMode:Bool = false
lazy var fetchedResultsController:NSFetchedResultsController = { () -> NSFetchedResultsController<Event> in
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Event")
request.sortDescriptors = [NSSortDescriptor(key:"eventDate",ascending:false)]
//request.predicate = NSPredicate(format:"contact.name = %@", self.)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let managedObjectContext = appDelegate?.persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"section", cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController as! NSFetchedResultsController<Event>
}()
viewDidLoad
中的 tableViewController
:
override func viewDidLoad() {
super.viewDidLoad()
//Load data
do{
try fetchedResultsController.performFetch()
}catch let error{
print("\(String(describing: Bundle.main.bundleIdentifier)):\(error)")
}
// Other code
}
@IBAction func unwindToEvents(segue: UIStoryboardSegue){
do{
try self.fetchedResultsController.performFetch()
}catch let error{
print("\(String(describing: Bundle.main.bundleIdentifier)):\(error)")
}
eventsTableView.reloadData()
}
NSFetched结果控制器和tableView
委托方法
func numberOfSections(in tableView: UITableView) -> Int {
let count = fetchedResultsController.sections?.count ?? 0
// some other code...
return count
}//end of numberOfSections
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let count = fetchedResultsController.sections?[section].numberOfObjects else{
return 0
}
return count
}
}//end of numberOfRowsInSection
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let eventCell = eventsTableView.dequeueReusableCell(withIdentifier: storyBoard.eventCell, for: indexPath) as! EventTableViewCell
eventCell.event = fetchedResultsController.object(at: indexPath) as Event
return eventCell
}
}//end of cellForRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.currentEventIndex = indexPath
let event = fetchedResultsController.object(at: indexPath)
performSegue(withIdentifier: storyBoard.showEventDetail, sender: event)
}
}//end of tableView didSelectRowAt
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in
// delete item at indexPath
let event = self.fetchedResultsController.object(at: indexPath)
self.fetchedResultsController.managedObjectContext.delete(event)
do{
self.deleteMode = true
try self.fetchedResultsController.managedObjectContext.save()
}catch let error as NSError{
print(error.localizedDescription)
}
}
let cancel = UITableViewRowAction(style: .normal, title: "Cancel") { (action, indexPath) in
//cancel swipe action
}
cancel.backgroundColor = UIColor.blue
return [delete,cancel]
}//end of editActionsForRowAt
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}//end of canEditRowAt
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard let sectionInfo = self.fetchedResultsController.sections?[section] else {
return " "
}
return sectionInfo.name //which will be the sectionNameKeyPath we provided earlier.
}
//MARK: NSFecthedResultscontroller delegate methods
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type{
case NSFetchedResultsChangeType.delete:
let sectionIndexSet = NSIndexSet(index:sectionIndex) as IndexSet
blockOps.append(BlockOperation(block:{
self.eventsTableView.deleteSections(sectionIndexSet, with: .automatic)
}))
default:
break
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type{
case .delete:
print(indexPath!.row,indexPath!.section)
blockOps.append(BlockOperation(block:{
self.eventsTableView.deleteRows(at: [indexPath!], with: .automatic)
}))
default:
break
}
}//end of didChange anObject at
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if self.deleteMode{//perform block op only for delete mode
self.deleteMode = false
eventsTableView.performBatchUpdates({
for operation in blockOps{
operation.start()
}
},completion: { (completion) in
})
}
}//end of controllerDidChangeContent
}//end of EventsViewController
集合视图:
var blockOps = [BlockOperation]()
lazy var fetchedResultsController:NSFetchedResultsController = { () -> NSFetchedResultsController<Event> in
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Event")
request.sortDescriptors = [NSSortDescriptor(key:"eventDate",ascending:false)]
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let managedObjectContext = appDelegate?.persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext!, sectionNameKeyPath: "section", cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController as! NSFetchedResultsController<Event>
}()
//Other code .........
@IBAction func didTapDeleteEvent(_ sender: UIButton) {
let currentItemIndex = eventDetailCollection.indexPathsForVisibleItems.first
if let indexPath = currentItemIndex{
let event = self.fetchedResultsController.object(at: indexPath)
self.fetchedResultsController.managedObjectContext.delete(event)
do{
self.deleteMode = true
try self.fetchedResultsController.managedObjectContext.save()
}catch let error as NSError{
print(error.localizedDescription)
}
}
}//end of didTapDelete
//The delegate methods
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type{
case .delete:
blockOps.append(BlockOperation(block:{
self.eventDetailCollection.deleteItems(at: [indexPath!])
}))
default:
break
}
}//end of didChange anObject at
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type{
case NSFetchedResultsChangeType.delete:
let sectionIndexSet = NSIndexSet(index:sectionIndex) as IndexSet
blockOps.append(BlockOperation(block:{
self.eventDetailCollection.deleteSections(sectionIndexSet)
}))
default:
break
}
}//end didChange SectionInfo
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if self.deleteMode{//perform block op only in delete mode
self.deleteMode = false
eventDetailCollection.performBatchUpdates({
for operation in blockOps{
operation.start()
}
},completion: { (completion) in
if let count = self.fetchedResultsController.fetchedObjects?.count {
if count < 1{
self.performSegue(withIdentifier: "detailToEvents", sender: self)
}
}
})
}
}//end of controllerDidChangeContent
答案 0 :(得分:0)
我的代码中遇到了同样的问题。我仍然是编程和Swift的新手,但这里是我用于从tableview中滑动删除的代码。也许这可以帮助你的代码。
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else {
return
}
// Fetch
let inventory = fetchedResultsController.object(at: indexPath)
// MARK: Delete
// tells managed object cotext to delete the selected object
fetchedResultsController.managedObjectContext.delete(inventory)
// goes to the data store and performs the action
try? fetchedResultsController.managedObjectContext.save()
// refetches the new data store
try? fetchedResultsController.performFetch()
// reloads data to the tableview
tableView.reloadData()
}