您应该仔细考虑是否要在每次更改时更新表视图。如果同时进行大量修改 - 例如,如果您正在从后台线程读取数据 - /.../,您可以实现controllerDidChangeContent :(在处理完所有挂起的更改时发送给委托)到重新加载表视图。
这正是我正在做的事情:我正在使用不同的ManagedObjectContext处理后台线程中的传入更改,并使用mergeChangesFromContextDidSaveNotification将结果合并到主线程MOC中。到目前为止一切都很好。
我选择不实现controller:didChangeObject:...而是希望执行文档建议的批量更新。
问题/问题:该文档没有详细说明如何实际实施批量更新?我应该在controllerDidChangeContent中调用[tableview reloadData]:还是有一种不那么干扰的方式可以让我免于完全重载?
我有一个想法:我可以注意到mergeChangesFrom ...包含已更改对象的通知,找出它们的索引路径,然后只调用tableview:ReloadRowsAtIndexPaths:for。但是有任何权威信息,建议或例子吗?或者只是[tableview reloadData]?
(旁白:控制器:didChangeObject:...当它收到一组批量更新时开始表现得非常不稳定,即使相同的更新代码[我现在放在后台线程中]在它运行之前运行良好主线程,但当然锁定UI。)
答案 0 :(得分:1)
我只需在reloadData
中致电controllerDidChangeContent:
。
为了动画表的个别更改,Apple的样板代码(iOS SDK 4.3.x)如下所示:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (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;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
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 configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
答案 1 :(得分:1)
我相信这就是你要找的东西:
而不是打电话
deleteSections
insertSections
reloadSections
deleteRowsAtIndexPaths
insertRowsAtIndexPaths
reloadRowsAtIndexPaths
当更改进入controller:didChangeObject:atIndexPath:forChangeType:newIndexPath
时,收集属性中的indexPaths和section。然后在controllerDidChangeContent:
。
修改强>
以下是链接中提到的方法略有不同,更加人为但又更紧凑的版本:
@property (nonatomic, strong) NSMutableArray *sectionChanges;
@property (nonatomic, strong) NSMutableArray *objectChanges;
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
NSMutableDictionary *sectionChange = [NSMutableDictionary new];
switch(type)
{
case NSFetchedResultsChangeInsert:
case NSFetchedResultsChangeDelete:
case NSFetchedResultsChangeUpdate:
sectionChange[@(type)] = @(sectionIndex);
break;
}
[self.sectionChanges addObject:sectionChange];
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
NSMutableDictionary *objectChange = [NSMutableDictionary new];
switch(type)
{
case NSFetchedResultsChangeInsert:
objectChange[@(type)] = newIndexPath;
break;
case NSFetchedResultsChangeDelete:
case NSFetchedResultsChangeUpdate:
objectChange[@(type)] = indexPath;
break;
case NSFetchedResultsChangeMove:
objectChange[@(type)] = @[indexPath, newIndexPath];
break;
}
[self.objectChanges addObject:objectChange];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
UITableView *tableView = self.tableView;
NSUInteger totalChanges = self.sectionChanges.count + self.objectChanges.count;
if (totalChanges > 0)
{
[tableView beginUpdates];
if (self.sectionChanges.count > 0)
{
for (NSDictionary *change in self.sectionChanges)
{
[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch (type)
{
case NSFetchedResultsChangeInsert:
[tableView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}];
}
}
else if (self.objectChanges > 0)
{
NSMutableArray *indexPathsForUpdatedObjects = [NSMutableArray new];
for (NSDictionary *change in self.objectChanges)
{
[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch (type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:@[obj] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:@[obj] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[indexPathsForUpdatedObjects addObject:obj];
//[tableView reloadRowsAtIndexPaths:@[obj] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
[tableView moveRowAtIndexPath:obj[0] toIndexPath:obj[1]];
break;
}
}];
}
[tableView endUpdates];
for (NSIndexPath *indexPath in indexPathsForUpdatedObjects)
if ([tableView.indexPathsForVisibleRows containsObject:indexPath])
[self updateCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
}
[self.sectionChanges removeAllObjects];
[self.objectChanges removeAllObjects];
}
else if (totalChanges < 1)
{
[tableView reloadData];
}
}
- (NSMutableArray *)sectionChanges
{
if (!_sectionChanges)
_sectionChanges = [NSMutableArray new];
return _sectionChanges;
}
- (NSMutableArray *)objectChanges
{
if (!_objectChanges)
_objectChanges = [NSMutableArray new];
return _objectChanges;
}
请注意,此解决方案并不完美,因为如果对部分进行更改并且这些对象位于未更新的部分中,则不会更新对象。应该很容易修复(并且与许多应用程序无关)。
答案 2 :(得分:0)
您可以记下数据源中的最后一个索引路径&amp;然后这样做 -
NSIndexPath *indexPath = nil;
NSMutableArray *newResults = [[NSMutableArray alloc] init];
if(gLastResultIndex < [self.dataSource count])
{
for(int i=(gLastResultIndex); i<[self.dataSource count]; i++)
{
indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[newResults addObject:indexPath];
}
[self.table beginUpdates];
[self.table insertRowsAtIndexPaths:newResults withRowAnimation:UITableViewRowAnimationNone];
[self.table endUpdates];
}
[newResults release];
这有选择地向现有UITableView
添加新行。重装表重新加载表中您可能不需要的所有单元格。我只在整个数据源发生变化时使用重载表...