Bar*_*zyk 43 nsfetchedresultscontroller ios swift ios9
这是一个错误:
CoreData:错误:严重的应用程序错误.在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托中捕获到异常.尝试使用userInfo(null)删除并重新加载相同的索引路径({length = 2,path = 0 - 0})
这是我的典型NSFetchedResultsControllerDelegate
:
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
let indexSet = NSIndexSet(index: sectionIndex)
switch type {
case .Insert:
tableView.insertSections(indexSet, withRowAnimation: .Fade)
case .Delete:
tableView.deleteSections(indexSet, withRowAnimation: .Fade)
case .Update:
fallthrough
case .Move:
tableView.reloadSections(indexSet, withRowAnimation: .Fade)
}
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: NSManagedObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
if let newIndexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
}
case .Delete:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
case .Update:
if let indexPath = indexPath {
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}
case .Move:
if let indexPath = indexPath {
if let newIndexPath = newIndexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
}
}
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates()
}
Run Code Online (Sandbox Code Playgroud)
在viewDidLoad()
:
private func setupOnceFetchedResultsController() {
if fetchedResultsController == nil {
let context = NSManagedObjectContext.MR_defaultContext()
let fetchReguest = NSFetchRequest(entityName: "DBOrder")
let dateDescriptor = NSSortDescriptor(key: "date", ascending: false)
fetchReguest.predicate = NSPredicate(format: "user.identifier = %@", DBAppSettings.currentUser!.identifier )
fetchReguest.sortDescriptors = [dateDescriptor]
fetchReguest.fetchLimit = 10
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchReguest, managedObjectContext: context, sectionNameKeyPath: "identifier", cacheName: nil)
fetchedResultsController.delegate = self
try! fetchedResultsController.performFetch()
}
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*n R 28
这似乎是iOS 9中的一个错误(仍然是测试版),也在Apple开发者论坛中进行了讨论
我可以通过Xcode 7 beta 3确认iOS 9 Simulator的问题.我观察到,对于更新的托管对象,didChangeObject:
委托方法被调用两次:一次是NSFetchedResultsChangeUpdate
事件,然后是NSFetchedResultsChangeMove
事件(和indexPath == newIndexPath
).
indexPath != newIndexPath
在上面的线程中添加显式检查似乎可以解决问题:
case .Move:
if indexPath != newIndexPath {
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*air 20
更新:在针对iOS 9.0或iOS 9.1(beta)SDK构建时,仅在iOS 8上出现所描述的问题.
在玩Xcode 7 beta 6(iOS 9.0 beta 5)之后,我想出了一些可怕的解决方法,它似乎有效.
您无法使用,reloadRowsAtIndexPaths
因为在某些情况下它被调用得太早并且可能导致不一致,而您应该手动更新您的单元格.
我仍然认为最好的选择就是打电话reloadData
.
我相信你可以毫不费力地调整我的代码以获得快速,我在这里有一个Objective-c项目.
@property NSMutableIndexSet *deletedSections, *insertedSections;
// ...
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
self.deletedSections = [[NSMutableIndexSet alloc] init];
self.insertedSections = [[NSMutableIndexSet alloc] init];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:sectionIndex];
switch(type) {
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
[self.deletedSections addIndexes:indexSet];
break;
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
[self.insertedSections addIndexes:indexSet];
break;
default:
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch(type) {
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:@[ newIndexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
// iOS 9.0b5 sends the same index path twice instead of delete
if(![indexPath isEqual:newIndexPath]) {
[self.tableView deleteRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:@[ newIndexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else if([self.insertedSections containsIndex:indexPath.section]) {
// iOS 9.0b5 bug: Moving first item from section 0 (which becomes section 1 later) to section 0
// Really the only way is to delete and insert the same index path...
[self.tableView deleteRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else if([self.deletedSections containsIndex:indexPath.section]) {
// iOS 9.0b5 bug: same index path reported after section was removed
// we can ignore item deletion here because the whole section was removed anyway
[self.tableView insertRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
}
break;
case NSFetchedResultsChangeUpdate:
// On iOS 9.0b5 NSFetchedResultsController may not even contain such indexPath anymore
// when removing last item from section.
if(![self.deletedSections containsIndex:indexPath.section] && ![self.insertedSections containsIndex:indexPath.section]) {
// iOS 9.0b5 sends update before delete therefore we cannot use reload
// this will never work correctly but at least no crash.
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self _configureCell:cell forRowAtIndexPath:indexPath];
}
break;
}
}
Run Code Online (Sandbox Code Playgroud)
仅限Xcode 7/iOS 9.0
在Xcode 7/iOS 9.0 NSFetchedResultsChangeMove
中仍然发送而不是"更新".
作为一个简单的解决方法,只需禁用该案例的动画:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableViewRowAnimation animation = UITableViewRowAnimationAutomatic;
switch(type) {
case NSFetchedResultsChangeMove:
// @MARK: iOS 9.0 bug. Move sent instead of update. indexPath = newIndexPath.
if([indexPath isEqual:newIndexPath]) {
animation = UITableViewRowAnimationNone;
}
[self.tableView deleteRowsAtIndexPaths:@[ indexPath ] withRowAnimation:animation];
[self.tableView insertRowsAtIndexPaths:@[ newIndexPath ] withRowAnimation:animation];
break;
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
mag*_*gma 16
关于在iOS8上发生的这种情况,使用针对iOS9编译的构建,除了indexPath==newIndexPath
其他一些答案所解决的问题之外,还会发生一些非常奇怪的事情.
该NSFetchedResultsChangeType
枚举有四个可能的值(与价值观的意见是我的):
public enum NSFetchedResultsChangeType : UInt {
case Insert // 1
case Delete // 2
case Move // 3
case Update // 4
}
Run Code Online (Sandbox Code Playgroud)
..但是,controller:didChangeObject:atIndexPath:forChangeType
有时使用无效值调用该函数0x0
.
Swift似乎默认为第一种switch
情况,所以如果你有以下结构:
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert: tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
case .Delete: tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
case .Update: tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.None)
case .Move: tableView.moveRowAtIndexPath(ip, toIndexPath: nip)
}
}
Run Code Online (Sandbox Code Playgroud)
..无效调用将导致插入,您将收到如下错误:
无效更新:第0节中的行数无效.更新(7)后现有部分中包含的行数必须等于更新前该部分中包含的行数(7),加上或减去数字从该部分插入或删除的行数(插入1个,删除0个)
简单地交换案例,以便第一个案例是一个相当无害的更新修复了问题:
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Update: tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.None)
case .Insert: tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
case .Delete: tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
case .Move: tableView.moveRowAtIndexPath(ip, toIndexPath: nip)
}
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是检查type.rawValue
无效值.
注意:虽然这解决了与OP发布的错误消息略有不同的错误消息,但问题是相关的; 有可能一旦你解决indexPath==newIndexPath
问题,这个就会弹出.此外,简化了上述代码块以说明顺序; guard
例如,缺少适当的块 - 请不要按原样使用它们.
致谢:这最初是由iCN7发现的,来源:Apple开发者论坛 - iOS 9 CoreData NSFetchedResultsController更新导致UICollectionView/UITableView中的空白行
Bar*_*zyk 10
由于某种原因,NSFetchedResultsController
调用.Update
后跟.Move
之后controllerWillChangeContent:
的调用.
它看起来像这样:开始更新 - > 更新 - > 移动 - > 结束更新.
仅在iOS 8.x下发生
在一个更新会话期间,重新加载相同的单元格并删除导致崩溃的单元格.
最简单的修复:
以下部分代码:
case .Update:
if let indexPath = indexPath {
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
Run Code Online (Sandbox Code Playgroud)
用...来代替:
case .Update:
if let indexPath = indexPath {
// 1. get your cell
// 2. get object related to your cell from fetched results controller
// 3. update your cell using that object
//EXAMPLE:
if let cell = tableView.cellForRowAtIndexPath(indexPath) as? WLTableViewCell { //1
let wishlist = fetchedResultsController.objectAtIndexPath(indexPath) as! WLWishlist //2
cell.configureCellWithWishlist(wishlist) //3
}
}
Run Code Online (Sandbox Code Playgroud)
真的很棒.
归档时间: |
|
查看次数: |
12115 次 |
最近记录: |