UICollectionView indexPathsForVisibleItems不会更新新的可见单元格

时间:2013-09-03 14:55:01

标签: ios uicollectionview nsindexpath

我有一个带有CollectionView的ViewController。加载视图时,正确显示可见单元格(9个单元格)。当我向下滚动时,我想通过调用partnerCollectionView的indexPathsForVisibleItems来加载带有loadImagesForOnscreenRows的集合视图中的可见项。但是当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

正如您在滚动后看到的那样,项目可见索引路径保持不变。有没有其他人遇到这个或想法的解决方案?或者我是否对收集视图的某些原则不合理?

非常感谢提前! 亲切的问候, 扬

1 个答案:

答案 0 :(得分:5)

如果您的目标是iOS 8.0及更高版本,则应使用collectionView:willDisplayCell:forItemAtIndexPath:启动下载。如果使用iOS 7.0,则应继续使用collectionView:cellForItemAtIndexPath:

imageLoaderDidFinishDownloading:回调中,您应该检查相关的索引路径是否仍然可见。如果是,则检索相应的单元格并更新其图像视图。如果单元格不可见,那么您的工作就完成了。为每个图像完成调用-reloadData会执行大量昂贵的工作,如果您的用户当前正在滚动表并重置其内容,则可能会出现重大的UX问题。您也可能多次执行UIImageJPEGRepresentation()工作,如果您在imageLoaderDidFinishDownloading:中执行此操作一次,然后将其缓存,则可以帮助您滚动性能。

因为看起来回调发生在后台线程上,所以请确保只操作主线程中的UICollectionView