UICollectionView indexPathsForVisibleItems does not update new visible cells

I have a ViewController with CollectionView inside. When the image is loaded, the visible cells (9 cells) are displayed correctly. When I scroll down, I want to load the visible elements in the collection using loadImagesForOnscreenRows by calling indexPathsForVisibleItems on partnerCollectionView. But when loadImagesForOnscreenRows indexPathsForVisibleItems always has the first 9 cells in it, even if cells 10 through 18 should be visible on the screen. The code I'm using is:

#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 

And this output result:

Initial display of visible items

 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 

After scrolling to paragraph 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 

As you can see after scrolling, the objects, the visible index paths, remain unchanged. Has anyone else come across this or an idea for a solution? Or do I not know some principle of data collection?

Thank you very much in advance! Regards, Yang

+8
ios uicollectionview nsindexpath
source share
1 answer

If you are targeting iOS 8.0 and above, you should use collectionView:willDisplayCell:forItemAtIndexPath: to start the download. If you are using iOS 7.0, you should continue to use collectionView:cellForItemAtIndexPath:

In your imageLoaderDidFinishDownloading: you need to check if the pointer path is all visible. If so, extract the appropriate cell and refresh its image. If the cell is not visible, your work will be done. Calling -reloadData for each image completion does a lot of expensive work and can have significant UX issues if your user is currently in the middle of a table scroll and you reset its contents. You also potentially do the UIImageJPEGRepresentation() job many times, this will help to improve scroll performance if you do this work once in imageLoaderDidFinishDownloading: and then save it.

Since it looks like the callback is happening in the background thread, make sure you only control the UICollectionView from the main thread.

+5
source share

All Articles