Ios 带有tableview的NSFetchedresultscontroller插入新节崩溃
我正在开发messages应用程序和所有保存在核心数据中的消息。我使用带有tableView的NSFetchedResultsController在屏幕上显示它们。我的nsfetchedResultsController中有一节,这是一周中的几天。我的意思是,一部分是今天的例子,另一部分是昨天的例子。当我想添加应该插入到新节中的新消息时,当我尝试将新对象插入到核心数据中,从而在tableview中生成新节时,我遇到了崩溃。这是我得到的日志: 无效更新:节0中的行数无效。更新(12)后现有节中包含的行数必须等于更新(8)前该节中包含的行数,加上或减去从该节中插入或删除的行数(1插入,1删除),加上或减去移入或移出该节的行数(0移入,0移出) 这是我的NSFETCHRESULTSCONTROLLER委托代码Ios 带有tableview的NSFetchedresultscontroller插入新节崩溃,ios,objective-c,uitableview,nsfetchedresultscontroller,Ios,Objective C,Uitableview,Nsfetchedresultscontroller,我正在开发messages应用程序和所有保存在核心数据中的消息。我使用带有tableView的NSFetchedResultsController在屏幕上显示它们。我的nsfetchedResultsController中有一节,这是一周中的几天。我的意思是,一部分是今天的例子,另一部分是昨天的例子。当我想添加应该插入到新节中的新消息时,当我尝试将新对象插入到核心数据中,从而在tableview中生成新节时,我遇到了崩溃。这是我得到的日志: 无效更新:节0中的行数无效。更新(12)后现有节中包含
- (void)dataLoaderDidChangeContent:(GISingleConversationDataLoader *)controller {
[self.tableView endUpdates];
if (_shouldScrollToBottomAfterUpdate) {
[self scrollToBottomAnimated:NO];
}
}
- (void)dataLoaderWillChangeContent:(GISingleConversationDataLoader *)controller {
[self.tableView beginUpdates];
}
- (void)dataLoader:(GISingleConversationDataLoader *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
if ([self.tableView numberOfSections]) {
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationBottom];
} else {
[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
}
_shouldScrollToBottomAfterUpdate = YES;
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
break;
}
}
数据源:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.dataProvider.fetchedResultsController.sections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([[self.dataProvider.fetchedResultsController sections] count] < 1) {
return 0;
}
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.dataProvider.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
GICDChatMessage *message = [self _messageAtIndexPath:indexPath];
GIChatMessageType type = [message.type integerValue];
NSString *kMessageTypeKey = [message isOutgoing] ? kOutgoingMessageKey : kIncomingMessageKey;
NSString *CellIdentifier; CellIdentifier = self.cellIdentifiers[@(type)][kMessageTypeKey];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
self.configureCellBlock(cell, message, indexPath);
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setBackgroundColor:tableView.backgroundColor];
return cell;
}
- (id)initWithConversation:(GIConversation *)conversation delegate:(id<GISingleConversationDataLoaderProtocol>)delegate {
self = [super init];
if (self) {
_conversation = conversation;
_delegate = delegate;
[self resultsController];
[_fetchedResultsController setDelegate:self];
[self performFetch];
}
return self;
}
- (NSPredicate *)predicate {
return [NSPredicate predicateWithFormat:@"conversationJid == %@", _conversation.jid];
}
- (NSString *)entityName {
return @"GICDChatMessage";
}
- (NSArray *)sortDescriptors {
return @[[NSSortDescriptor sortDescriptorWithKey:@"receivedAt" ascending:YES]];
}
- (NSFetchedResultsController*)resultsController {
if (_fetchedResultsController == nil) {
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:[self fetchRequest] managedObjectContext:[self context] sectionNameKeyPath:@"sectionNameKeyPath" cacheName:nil];
}
return _fetchedResultsController;
}
-(void)performFetch {
if (_fetchedResultsController) {
[_fetchedResultsController performFetch:nil];
[_delegate dataLoaderDidChangeContent:self];
}
}
- (NSFetchRequest*)fetchRequest {
NSFetchRequest *result = [NSFetchRequest fetchRequestWithEntityName:[self entityName]];
NSPredicate *mainPredicate = [self predicate];
result.predicate = mainPredicate;
result.sortDescriptors = [self sortDescriptors];
return result;
}
- (NSManagedObjectContext *)context {
return [[GICoreDataManager sharedInstance] mainContext];
}
#pragma mark - fetch change
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
[_delegate dataLoader:self
didChangeObject:anObject
atIndexPath:indexPath
forChangeType:type
newIndexPath:newIndexPath];
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[_delegate dataLoaderWillChangeContent:self];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[_delegate dataLoaderDidChangeContent:self];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
[_delegate dataLoader:self didChangeSection:sectionInfo atIndex:sectionIndex forChangeType:type];
}
它只是每天对邮件进行排序
你知道是什么导致了这个问题,还是我做错了什么
我的英语不好,如果你不明白我的意思,请问我,我会尽量解释得更清楚。你需要执行DIDChange部分:
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
您尚未实现
NSFetchedResultsControllerDelegate
协议的任何方法。相反,您编写了具有类似名称的自定义方法。例如,以下是您发布的方法之一:
- (void)dataLoaderWillChangeContent:(GISingleConversationDataLoader *)controller {
[self.tableView beginUpdates];
}
尽管上述方法的名称类似于NSFetchedResultsControllerDelegate
协议的方法,但它不是同一个方法,因此您的fetchedResultsController永远不会调用它。如果希望获取的结果控制器调用它,则需要实现以下方法声明:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;
同样,您应该更改代码该部分中其他方法的名称和签名,以匹配核心数据提供的实际API。聚合更新
- (void)dataLoaderDidChangeContent:(GISingleConversationDataLoader *)controller {
[self.tableView endUpdates];
if (_shouldScrollToBottomAfterUpdate) {
[self scrollToBottomAnimated:NO];
}
}
- (void)dataLoaderWillChangeContent:(GISingleConversationDataLoader *)controller {
[self.tableView beginUpdates];
}
- (void)dataLoader:(GISingleConversationDataLoader *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
if ([self.tableView numberOfSections]) {
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationBottom];
} else {
[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
}
_shouldScrollToBottomAfterUpdate = YES;
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
break;
}
}
然后在controllerDidChangeContent
中,您需要处理所有收到的更新并转发到UITableView
此代码非常适合我:
class SomeVC: UITableViewController {
/* ... */
// Add properties for each expected update type
var insertedSectionIndexes = NSMutableIndexSet()
var deletedSectionIndexes = NSMutableIndexSet()
var deletedRowIndexPaths: Set<NSIndexPath> = []
var insertedRowIndexPaths: Set<NSIndexPath> = []
var updatedRowIndexPaths: Set<NSIndexPath> = []
}
extension SomeVC: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent (controller: NSFetchedResultsController) {}
func controller(controller: NSFetchedResultsController,
didChangeObject anObject: NSManagedObject,
atIndexPath indexPath: NSIndexPath?,
forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?) {
switch type {
case .Insert: insertedRowIndexPaths.insert(indexPath!)
case .Update: updatedRowIndexPaths.insert(indexPath!)
case .Move:
if newIndexPath?.section != indexPath?.section {
insertedRowIndexPaths.insert(newIndexPath!)
deletedRowIndexPaths.insert(indexPath!)
}
case .Delete: deletedRowIndexPaths.insert(indexPath!)
}
}
func controller(controller: NSFetchedResultsController,
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
atIndex sectionIndex: Int,
forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Insert: insertedSectionIndexes.addIndex(sectionIndex)
case .Delete: deletedSectionIndexes.addIndex(sectionIndex)
default:()
}
}
func controllerDidChangeContent (controller: NSFetchedResultsController) {
tableView?.beginUpdates()
tableView?.deleteSections(deletedSectionIndexes, withRowAnimation: .None)
tableView?.insertSections(insertedSectionIndexes, withRowAnimation: .None)
updatedRowIndexPaths.subtractInPlace(deletedRowIndexPaths)
updatedRowIndexPaths.subtractInPlace(insertedRowIndexPaths)
tableView?.deleteRowsAtIndexPaths(Array(deletedRowIndexPaths), withRowAnimation: .Automatic)
tableView?.insertRowsAtIndexPaths(Array(insertedRowIndexPaths), withRowAnimation: .Automatic)
tableView?.reloadRowsAtIndexPaths(Array(updatedRowIndexPaths), withRowAnimation: .Automatic)
tableView?.endUpdates()
// Do not forget to clean up!
insertedSectionIndexes.removeAllIndexes()
deletedSectionIndexes.removeAllIndexes()
deletedRowIndexPaths = []
insertedRowIndexPaths = []
updatedRowIndexPaths = []
}
}
类SomeVC:UITableViewController{
/* ... */
//为每个预期的更新类型添加属性
var insertedSectionIndexes=NSMutableIndexSet()
var deletedSectionIndexes=NSMutableIndexSet()
var deletedRowIndexPaths:Set=[]
var insertedRowIndexPaths:Set=[]
var updatedRowIndexPaths:Set=[]
}
扩展名SomeVC:NSFetchedResultsControllerDelegate{
func controllerWillChangeContent(控制器:NSFetchedResultsController){}
func控制器(控制器:NSFetchedResultsController,
didChangeObject对象:NSManagedObject,
atIndexPath indexPath:NSIndexPath?,
forChangeType类型:NSFetchedResultsChangeType,
newindepath:nsindepath?){
开关类型{
case.Insert:insertedrowindexpath.Insert(indexPath!)
Update:updatedRowIndexPath.insert(indexPath!)
案件.动议:
如果newIndexPath?.section!=indexPath?.section{
insertedrowindexpath.insert(newIndexPath!)
deletedRowIndexPaths.insert(indexPath!)
}
case.Delete:deletedRowIndexPaths.insert(indexPath!)
}
}
func控制器(控制器:NSFetchedResultsController,
didChangeSection节信息:NSFetchedResultsSectionInfo,
a索引部分索引:Int,
forChangeType类型:NSFetchedResultsChangeType){
开关类型{
case.Insert:insertedSectionIndex.addIndex(sectionIndex)
case.Delete:deletedSectionIndex.addIndex(sectionIndex)
默认值:()
}
}
func controllerDidChangeContent(控制器:NSFetchedResultsController){
tableView?.BeginUpdate()
tableView?.deleteSections(deletedSectionIndexes,withRowAnimation:.None)
tableView?.insertSections(insertedSectionIndexes,带行动画:.None)
updatedRowIndexPaths.subtractInPlace(deletedRowIndexPaths)
updatedRowIndexPaths.subtractInPlace(insertedRowIndexPaths)
tableView?.deleteRowsAtIndexPaths(数组(deletedRowIndexPaths),带行动画:。自动)
tableView?.insertRowsAtIndexPaths(数组(InsertDroWindExpaths),带行动画:。自动)
tableView?.reloadRowsAtIndexPaths(数组(updatedRowIndexPaths),带RowAnimation:.Automatic)
tableView?.endUpdates()
//别忘了打扫卫生!
insertedSectionIndexes.removeAllIndexes()
deletedSectionIndexes.removeAllIndexes()
deletedRowIndexPaths=[]
insertedRowIndexPaths=[]
updatedRowIndexPaths=[]
}
}
注意:来自Xcode 7 beta 2的代码,有些方法可能会被称为不同的我们还可以看到tableview的数据源方法吗?我怀疑它与fetched results controller不同步。由于这些方法几乎都是直接从引用中提取的样板文件(应该是这样的,没有理由真正更改它们),dataSource方法是下一个可能的候选方法。NSFetchedResultSchangeSheinsert案例不是样板文件代码,有什么原因吗?那么控制器:didChangeSection:delegate方法呢?这是我的数据源:)不,我没有实现didChangeSection。我应该吗?我添加了didChangeSection,但仍然存在相同的问题:/I添加了didChangeSection,并且解决了部分的“是”问题,但现在它停止在这里
[tableView InsertRowsAndExpaths:[NSArray arrayWithObject:newIndexPath]with RowAnimation:UITableViewRowAnimationBottom]代码>并且它不想再继续了:/n您是否已经去掉了执行此操作的部分:
class SomeVC: UITableViewController {
/* ... */
// Add properties for each expected update type
var insertedSectionIndexes = NSMutableIndexSet()
var deletedSectionIndexes = NSMutableIndexSet()
var deletedRowIndexPaths: Set<NSIndexPath> = []
var insertedRowIndexPaths: Set<NSIndexPath> = []
var updatedRowIndexPaths: Set<NSIndexPath> = []
}
extension SomeVC: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent (controller: NSFetchedResultsController) {}
func controller(controller: NSFetchedResultsController,
didChangeObject anObject: NSManagedObject,
atIndexPath indexPath: NSIndexPath?,
forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?) {
switch type {
case .Insert: insertedRowIndexPaths.insert(indexPath!)
case .Update: updatedRowIndexPaths.insert(indexPath!)
case .Move:
if newIndexPath?.section != indexPath?.section {
insertedRowIndexPaths.insert(newIndexPath!)
deletedRowIndexPaths.insert(indexPath!)
}
case .Delete: deletedRowIndexPaths.insert(indexPath!)
}
}
func controller(controller: NSFetchedResultsController,
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
atIndex sectionIndex: Int,
forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Insert: insertedSectionIndexes.addIndex(sectionIndex)
case .Delete: deletedSectionIndexes.addIndex(sectionIndex)
default:()
}
}
func controllerDidChangeContent (controller: NSFetchedResultsController) {
tableView?.beginUpdates()
tableView?.deleteSections(deletedSectionIndexes, withRowAnimation: .None)
tableView?.insertSections(insertedSectionIndexes, withRowAnimation: .None)
updatedRowIndexPaths.subtractInPlace(deletedRowIndexPaths)
updatedRowIndexPaths.subtractInPlace(insertedRowIndexPaths)
tableView?.deleteRowsAtIndexPaths(Array(deletedRowIndexPaths), withRowAnimation: .Automatic)
tableView?.insertRowsAtIndexPaths(Array(insertedRowIndexPaths), withRowAnimation: .Automatic)
tableView?.reloadRowsAtIndexPaths(Array(updatedRowIndexPaths), withRowAnimation: .Automatic)
tableView?.endUpdates()
// Do not forget to clean up!
insertedSectionIndexes.removeAllIndexes()
deletedSectionIndexes.removeAllIndexes()
deletedRowIndexPaths = []
insertedRowIndexPaths = []
updatedRowIndexPaths = []
}
}