在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data
Posted
技术标签:
【中文标题】在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data【英文标题】:Saving data from the web into Core Data while updating UITableView with NSFetchedResultsController 【发布时间】:2013-07-04 17:09:10 【问题描述】:我正在使用 HTTP GET 方法从网络中提取数据。然后我从网络上获取 JSON 数据并将其保存到 CoreData。我有一个带有自定义UITableViewCells
的UITableView
,它使用NSFetchedResultsController
进行了更新。
但是,当我去保存数据时,tableView 只会更新屏幕上已经存在的内容,当我向下滚动时,tableView 会变成白色。
在日志中我收到了一些错误:
CoreData:错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间,从 NSFetchedResultsController 的委托中捕获了异常。尝试使用 userInfo (null) 删除并重新加载相同的索引路径 (length = 2, path = 0 - 43)
这是NSFetchedResultsControllerDelegate
在didChangeObject
方法中使用的代码:
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.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
关于我能做什么的任何想法?
谢谢!
【问题讨论】:
【参考方案1】:你的呢?
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
您可以使用我的,它肯定可以工作,我在几个项目中使用过它。 只需让你的 tableViewController 子类化这个。 然后初始化并将您的 fetchedResultsController 传递给您的 tableViewController 并在那里调用:
[self setupFetchedResultsController:yourFetchResultsController];
SMCoreDataTableViewController.m:
#import "SMCoreDataTableViewController.h"
@interface SMCoreDataTableViewController ()
@property (nonatomic) BOOL beganUpdates;
@end
@implementation SMCoreDataTableViewController
- (void)performFetch
if (self.fetchedResultsController)
if (self.fetchedResultsController.fetchRequest.predicate)
if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
else
if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
NSError *error;
[self.fetchedResultsController performFetch:&error];
if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
else
if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self.tableView reloadData];
- (void)setupFetchedResultsController:(NSFetchedResultsController *)newfrc
NSFetchedResultsController *oldfrc = _fetchedResultsController;
if (newfrc != oldfrc)
_fetchedResultsController = newfrc;
newfrc.delegate = self;
if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title))
self.title = newfrc.fetchRequest.entity.name;
if (newfrc)
if (self.debug) NSLog(@"[%@ %@] %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? @"updated" : @"set");
[self performFetch];
else
if (self.debug) NSLog(@"[%@ %@] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self.tableView reloadData];
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
return [[self.fetchedResultsController sections] count];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
return [self.fetchedResultsController sectionIndexTitles];
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
[self.tableView beginUpdates];
self.beganUpdates = YES;
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
switch(type)
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
switch(type)
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
if (self.beganUpdates) [self.tableView endUpdates];
- (void)endSuspensionOfUpdatesDueToContextChanges
_suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend
if (suspend)
_suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
else
[self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
@end
SMCoreDataTableViewController.h:
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
@interface SMCoreDataTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext;
@property BOOL debug;
- (void)setupFetchedResultsController:(NSFetchedResultsController *)fetchedResultsController;
@end
【讨论】:
以上是关于在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data的主要内容,如果未能解决你的问题,请参考以下文章
NSFetchedresultscontroller 与 tableview 插入新部分崩溃
更新相关 NSManagedObject 时更新 UITableView
NSFetchedResultsController:具有相同键的两个排序描述符不起作用。一个降序用于部分,另一个升序用于行