NSFetchedResultsController 刷新 UITableView 条目而不重新加载口吃
Posted
技术标签:
【中文标题】NSFetchedResultsController 刷新 UITableView 条目而不重新加载口吃【英文标题】:NSFetchedResultsController refresh UITableView entries without reload stutter 【发布时间】:2012-02-07 02:25:04 【问题描述】:我有一个由 NSFetchedResultsController 驱动的 UITableView。我调用了一个方法来创建一个 NSPredicate 来过滤从今天开始具有时间戳的我的实体。在我的 NSFetchedResultsController 中,我应用了谓词。
if (appDelegate.displayTodayOnly)
[fetchRequest setPredicate:datePredicate];
这一切都很好。但是,有时我需要查看“今天”之前的条目。当它已经在表格的开头时,我通过在表格视图上向下滑动来做到这一点。
-(void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
if (self.tableView.contentOffset.y<0)
if (appDelegate.displayTodayOnly)
[appDelegate setDisplayTodayOnly:NO];
NSInteger resultsOffset = [[self.fetchedResultsController fetchedObjects] count];
//[self.tableView beginUpdates];
[__fetchedResultsController release];
__fetchedResultsController=nil;
NSError *error;
if (![[self fetchedResultsController] performFetch:&error])
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
//[self.tableView endUpdates];
NSInteger fullCount = [[self.fetchedResultsController fetchedObjects] count];
if (fullCount>resultsOffset)
NSUInteger sect = [[self.fetchedResultsController sections] count]-2;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:sect];
NSUInteger rows = [sectionInfo numberOfObjects]-1;
NSIndexPath *indexing = [NSIndexPath indexPathForRow:rows inSection:sect];
NSLog(@"IndexPath = section:%d row:%d",sect,rows);
[self.tableView reloadData];
[self performSelector:@selector(scrollToIndexPath:) withObject:indexing afterDelay:0.0001];
这也有效,但不优雅。它丢弃 fetchedResultsController,并重新加载表,从与条目相关的时间开始的条目开始。然后,我让它滚动到上一个表条目上方的索引路径。
我希望在当前表顶部之前加载和插入条目。有谁知道一种优雅的方式来改变 fetch 以实现这一点?
【问题讨论】:
【参考方案1】:我通常做的是设置
self.fetchedResultsController = nil;
并重新加载。它非常高效并且代码最少(假设您像在 Xcode 模板中一样懒惰地加载和创建获取的结果控制器)。
【讨论】:
是的,它有效,这就是我的 scrollViewDidEndDragging 方法中发生的事情。我试图了解是否有办法减轻重新加载,以免结果跳来跳去。【参考方案2】:我发布我的解决方案,希望它可以帮助其他有需要的人。
我仍在将 NSFetchedResultsController
实例设置为 nil,并获得一个具有不同谓词的新实例。然后,我没有重新加载表格(导致迷失方向的跳转),而是在 for(loop) 中手动调用[self.tableView insertSections:[NSIndexSet indexSetWithIndex:i] withRowAnimation:UITableViewRowAnimationNone];
,它几乎直接来自<NSFetchedResultsControllerDelegate>
方法的样板文件controllerWillChangeContent:
。这是有效的,因为最后一部分总是今天的部分。如果今天没有任何条目,我需要重新加载数据,因为插入的节数会不一致,因为今天的空节算作一个节。
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
if (self.tableView.contentOffset.y<0)
if (appDelegate.displayTodayOnly)
[appDelegate setTodayOnlyMemory:NO];
NSUInteger resultsOffset = [[self.fetchedResultsController fetchedObjects] count];
[__fetchedResultsController release];
__fetchedResultsController = nil;
self.fetchedResultsController = nil;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
NSLog(@"error in fetched results controller of Memory Notes");
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
if (resultsOffset)
[self.tableView beginUpdates];
NSInteger afCount = ([[self.fetchedResultsController sections] count]-1);
for (NSInteger i=0; i<afCount; i++)
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:i] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
else
[self.tableView reloadData];
[self performSelector:@selector(scrollToSpot) withObject:nil afterDelay:0.01f];
此外,这是我的 scrollToSpot 方法,用于在正确的位置结束。
-(void)scrollToSpot
NSArray *sectArray = [self.fetchedResultsController sections];
if ([sectArray count]>1)
id <NSFetchedResultsSectionInfo> sectionInfo = [sectArray objectAtIndex:([sectArray count]-2)];
NSInteger theRow = [sectionInfo numberOfObjects]-1;
NSIndexPath* ipath = [NSIndexPath indexPathForRow:theRow inSection:([sectArray count]-2)];
NSLog(@"Row:%d",ipath.row);
[self.tableView scrollToRowAtIndexPath:ipath atScrollPosition:UITableViewScrollPositionTop animated:NO];
NSLog(@"ContentOffset:%.0f",self.tableView.contentOffset.y);
【讨论】:
以上是关于NSFetchedResultsController 刷新 UITableView 条目而不重新加载口吃的主要内容,如果未能解决你的问题,请参考以下文章