UICollectionView indexPathsForVisibleItems 不更新新的可见单元格
Posted
技术标签:
【中文标题】UICollectionView indexPathsForVisibleItems 不更新新的可见单元格【英文标题】:UICollectionView indexPathsForVisibleItems don't update new visible cells 【发布时间】:2013-09-03 14:55:01 【问题描述】:我有一个 ViewController,里面有一个 CollectionView。当视图加载时,可见单元格(9 个单元格)正确显示。当我向下滚动时,我想通过调用 partnerCollectionView 的 indexPathsForVisibleItems 来使用 loadImagesForOnscreenRows 加载 collectionview 中的可见项。但是当 loadImagesForOnscreenRows 时 indexPathsForVisibleItems 总是包含前 9 个单元格,即使第 10 到 18 个单元格应该在屏幕上可见。我使用的代码是:
#import "PartnerListViewController.h"
#import "AppDelegate.h"
#import "Partner.h"
#import "ImageLoader.h"
#import "PartnerDetailViewController.h"
@interface PartnerListViewController ()
@end
@implementation PartnerListViewController
@synthesize lblTitle;
@synthesize partnerCollectionView;
@synthesize imageDownloadsInProgress;
@synthesize fetchedResultsController;
@synthesize managedObjectContext;
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view.
AppDelegate * appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
imageDownloadsInProgress = [NSMutableDictionary dictionary];
appDelegate = nil;
[self setupFetchedResultsController];
[partnerCollectionView reloadData];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
#pragma mark - Collection view data source
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
return [[fetchedResultsController sections] count];
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
static NSString *CellIdentifier = @"PartnerCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
Partner *partner = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:100];
if (!partner.image)
imageView.image = [UIImage imageNamed:@"annotationMap.png"];
if (self.partnerCollectionView.dragging == NO && self.partnerCollectionView.decelerating == NO)
[self startDownload:partner.imageUrl forIndexPath:indexPath];
else
imageView.image = [UIImage imageWithData:partner.image];
UILabel *lblTitlePartner = (UILabel *)[cell viewWithTag:101];
lblTitlePartner.text = partner.title;
lblTitlePartner.font = [UIFont fontWithName:fontName size:10];
return cell;
#pragma mark - Table cell image support
- (void)startDownload:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath
NSLog(@"startDownload:%ld", (long)indexPath.row);
ImageLoader *imageLoader = [imageDownloadsInProgress objectForKey:indexPath];
imageLoader = [[ImageLoader alloc] init];
imageLoader.urlString = urlString;
imageLoader.indexPathTableView = indexPath;
imageLoader.delegate = (id)self;
[imageDownloadsInProgress setObject:imageLoader forKey:indexPath];
[imageLoader startDownload];
// this method is used in case the user scrolled into a set of cells that don't have their app icons yet
- (void)loadImagesForOnscreenRows
NSArray *visiblePaths = [self.partnerCollectionView indexPathsForVisibleItems];
NSMutableArray *rowsArray = [NSMutableArray arrayWithCapacity:[visiblePaths count]];
[visiblePaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop)
NSLog(@"loadImagesForOnscreenRows1%@", @(indexPath.item));
[rowsArray addObject:@(indexPath.item)];
];
for (NSIndexPath *indexPath in visiblePaths)
NSLog(@"loadImagesForOnscreenRows2:%ld", (long)indexPath.row);
Partner *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (!item.image) // avoid the app icon download if the app already has an icon
[self startDownload:item.imageUrl forIndexPath:indexPath];
// called by our ImageDownloader when an icon is ready to be displayed
- (void)imageLoaderDidFinishDownloading:(NSIndexPath *)indexPath
NSLog(@"imageLoaderDidFinishDownloading:%ld", (long)indexPath.row);
ImageLoader *imageLoader = [imageDownloadsInProgress objectForKey:indexPath];
if (imageLoader != nil)
// Save the newly loaded image
Partner *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
item.image = UIImageJPEGRepresentation(imageLoader.image, 1.0);
[self performSelectorOnMainThread:@selector(saveItem) withObject:nil waitUntilDone:YES];
[self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];
- (void)saveItem
[self.managedObjectContext save:nil];
- (void)reloadData
[self.partnerCollectionView reloadData];
#pragma mark deferred image loading (UIScrollViewDelegate)
// Load images for all onscreen rows when scrolling is finished
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
if (!decelerate)
[self loadImagesForOnscreenRows];
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
[self loadImagesForOnscreenRows];
- (void)setupFetchedResultsController
// 1 - Decide what Entity you want
NSString *entityName = @"Partner"; // Put your entity name here
NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:NO selector:@selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error])
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
@end
这会导致输出:
可见项目的初始显示
2013-09-02 06:45:21.940 [2564:c07] startDownload:0
2013-09-02 06:45:21.943 [2564:c07] imageLoaderDidFinishDownloading:0
2013-09-02 06:45:21.950 [2564:c07] startDownload:1
2013-09-02 06:45:21.951 [2564:c07] imageLoaderDidFinishDownloading:1
2013-09-02 06:45:21.958 [2564:c07] startDownload:2
2013-09-02 06:45:21.959 [2564:c07] imageLoaderDidFinishDownloading:2
2013-09-02 06:45:21.965 [2564:c07] startDownload:3
2013-09-02 06:45:22.063 [2564:c07] imageLoaderDidFinishDownloading:3
2013-09-02 06:45:22.072 [2564:c07] startDownload:4
2013-09-02 06:45:22.073 [2564:c07] imageLoaderDidFinishDownloading:4
2013-09-02 06:45:22.081 [2564:c07] startDownload:5
2013-09-02 06:45:22.082 [2564:c07] imageLoaderDidFinishDownloading:5
2013-09-02 06:45:22.089 [2564:c07] startDownload:6
2013-09-02 06:45:22.090 [2564:c07] imageLoaderDidFinishDownloading:6
2013-09-02 06:45:22.098 [2564:c07] startDownload:7
2013-09-02 06:45:22.099 [2564:c07] imageLoaderDidFinishDownloading:7
2013-09-02 06:45:22.104 [2564:c07] startDownload:8
2013-09-02 06:45:22.163 [2564:c07] imageLoaderDidFinishDownloading:8
滚动到第 10 到 19 项后:
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:8
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:0
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:1
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:6
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:2
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:3
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:4
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:5
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:7
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:8
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:0
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:1
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:6
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:2
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:3
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:4
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:5
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:7
正如您在滚动后看到的那样,项目可见的索引路径确实保持不变。有没有其他人遇到过这个或解决方案的想法?还是我理解collectionview的一些原则错了?
提前非常感谢! 亲切的问候, 一月
【问题讨论】:
我不明白loadImagesForOnscreenRows
的目的。在cellForRowAtIndexPath
中设置图像(如果可用)。否则,在imageLoaderDidFinishDownloading
中,您可以重新加载索引路径,这将再次调用cellForRowAtIndexPath
,或者如果图像仍在屏幕上,您可以直接在单元格上设置图像。这不会覆盖它吗?
嗨蒂莫西,cellForRowAtIndexPath
加载视图中可见的单元格,在我的例子中是前九个。当我开始滚动时,即将到来的单元格不会被填充,而是调用scrollViewDidEndDragging
,它将调用loadImagesForOnscreenRows
,就像在表格视图中一样。在这个函数中,我想获取可见的索引路径indexPathsForVisibleItems
,并开始下载图像,一旦下载,重新加载索引路径。但是indexPathsForVisibleItems
总是返回前九个。还是我做错了什么。对于表格,这种方式可以正常工作。
没有图像的单元格通常会在该单元格的下载完成时单独填充,在您的情况下会发生在imageLoaderDidFinishDownloading
中。鉴于此,我不明白loadimageforOnscreenRows
的意思。换句话说,你为什么不填写imageLoaderDidFinishDownloading
的单元格?
当我加载我的视图时,cellForRowAtIndexPath
只被称为前九个单元格,即可见的单元格。 NSLog
in cellForRowAtIndexPath
只打印 0 到 8。因此其他单元格不会开始下载,在获取结果后重新加载 collectionView 只会影响可见单元格。这就是为什么我教我应该在scrollViewDidEndDragging
上调用loadimageforOnscreenRows
,这样它就可以加载向下滚动后变得可见的单元格的图像。因为如果我将scrollViewDidEndDragging
放在评论中,滚动后它完全没有任何作用。 :)
cellForRowAtIndexPath
总是在单元格显示在屏幕上之前被调用。否则,将没有任何东西可以显示。此外,您似乎没有实现 NSFetchedResultsControllerDelegate
方法,所以我不清楚您为什么还要使用 NSFetchedResultsController
,因为没有委托,它无法控制任何东西。我错过了什么吗?
【参考方案1】:
如果您的目标是 ios 8.0 及更高版本,您应该使用 collectionView:willDisplayCell:forItemAtIndexPath:
开始下载。如果使用 iOS 7.0,您应该继续使用collectionView:cellForItemAtIndexPath:
。
在您的imageLoaderDidFinishDownloading:
回调中,您应该检查相关索引路径是否仍然可见。如果是,则检索相应的单元格并更新其图像视图。如果单元格不可见,那么您的工作就完成了。为每个图像完成调用-reloadData
会做大量昂贵的工作,并且如果您的用户当前正在表格的中间滚动并且您重置其内容,则可能会出现严重的用户体验问题。您还可能多次执行UIImageJPEGRepresentation()
工作,如果您在imageLoaderDidFinishDownloading:
中执行此工作一次然后缓存它,这将有助于您的滚动性能。
由于看起来回调发生在后台线程上,请确保您只在主线程中操作 UICollectionView
。
【讨论】:
以上是关于UICollectionView indexPathsForVisibleItems 不更新新的可见单元格的主要内容,如果未能解决你的问题,请参考以下文章