UICollectionView indexPathsForVisibleItems nicht aktualisieren, neue sichtbare Zellen
Ich habe einen ViewController mit einem CollectionView im inneren. Wenn die Ansicht geladen wird, die sichtbaren Zellen (9 Zellen) werden korrekt angezeigt. Wenn ich nach unten scrollen, will ich laden Sie die sichtbaren Elemente in der collectionview mit loadImagesForOnscreenRows durch den Aufruf der indexPathsForVisibleItems für partnerCollectionView. Aber wenn loadImagesForOnscreenRows die indexPathsForVisibleItems hat immer die ersten 9 Zellen, auch dann, wenn die Zellen 10 bis 18 sollte auf dem Bildschirm sichtbar. Der code, den ich verwenden ist:
#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
Und das Ergebnis in der Ausgabe:
Erste Karte sichtbaren Elementen
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
Nach dem scrollen zu Artikel 10 um 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
Wie Sie sehen können nach dem scrollen werden die Elemente sichtbar, index-Pfade bleiben gleich. Hat sonst jemand Probleme oder eine Idee für eine Lösung? Oder bin ich unsterstanding einige Prinzip der collectionview falsch?
Vielen Dank im Voraus!
Mit freundlichen GRÜßEN,
Jan
- Ich habe nicht den Zweck
loadImagesForOnscreenRows
. IncellForRowAtIndexPath
Sie das Bild, wenn es verfügbar ist. Ansonsten, inimageLoaderDidFinishDownloading
laden Sie die index-Pfad, die rufencellForRowAtIndexPath
wieder oder Sie können das Bild auf die Zelle direkt, ob es noch auf dem Bildschirm. Wäre das nicht abdecken? - Hallo Timothy,
cellForRowAtIndexPath
lädt die Zellen, die in der Ansicht sichtbar sind, in meinem Fall die ersten neun. Wenn ich anfange zu scrollen, das kommende Zellen nicht ausgefüllt werden, stattscrollViewDidEndDragging
genannt wird, rufenloadImagesForOnscreenRows
wie in tableviews. In dieser Funktion möchte ich, um die indexpaths,indexPathsForVisibleItems
, sichtbar sind, und starten Sie das herunterladen der Bilder, sobald diese heruntergeladen sind, laden Sie die indexpaths. AberindexPathsForVisibleItems
gibt immer den ersten neun. Oder mache ich etwas komplett falsch. Mit Tabellen funktioniert es auf diese Weise. - Die Zellen, die nicht über Bilder würden in der Regel erhalten Sie individuell ausgefüllt, wenn der download für die Zelle abgeschlossen ist, was in deinem Fall passieren würde, in
imageLoaderDidFinishDownloading
. Angesichts dieser, ich weiß nicht, was der Punkt vonloadimageforOnscreenRows
. In anderen Worten, warum sind Sie nicht ausfüllen in der Zelle, inimageLoaderDidFinishDownloading
? - Wenn ich die Last meiner Sicht
cellForRowAtIndexPath
wird nur aufgerufen, für die ersten neun Zellen, die sichtbaren sind. EinNSLog
imcellForRowAtIndexPath
druckt nur 0 bis 8. Damit die anderen Zellen nicht den Download starten, Neuladen der collectionView nach Holen das Ergebnis wirkt sich nur auf die sichtbaren Zellen. Das ist, warum ich lehrte ich es nennen sollloadimageforOnscreenRows
aufscrollViewDidEndDragging
, so kann das laden der Bilder für die Zellen, die sichtbar nach unten scrollen. Weil wenn ich diescrollViewDidEndDragging
im Kommentar, es tut sich nichts nach dem scrollen. 🙂 cellForRowAtIndexPath
wird immer aufgerufen, bevor eine Zelle auf dem Bildschirm angezeigt wird. Sonst würde nichts angezeigt. Auch, Sie scheinen es nicht zu sein, die Umsetzung derNSFetchedResultsControllerDelegate
Methoden, so bin ich mir unklar, warum man selbst mit einerNSFetchedResultsController
da, ohne einen Delegaten, es ist keine Kontrolle, nichts. Bin ich etwas fehlt?- Hallo Timothy, ich habe ein anderes problem. Mit
reloadData
auf den collectionview inimageLoaderDidFinishDownloading
nicht nennencellForRowAtIndexPath
für die Zellen sichtbar werden. Wenn ichreloadItemsAtIndexPaths
,cellForRowAtIndexPath
genannt wird, für die Zellen sichtbar werden. Das problem ist nur, dass die erste Zelle jeder Zeile hält seine Standard-Bild-und die anderen sind erfrischt richtig. Wenn ich scroll die ersten Zellen aus dem Bildschirm heraus-und wieder an, erscheinen Sie richtig. So der download ist richtig, nur erfrischend scheint nicht zu funktionieren. Haben Sie eine Idee? Vielen Dank im Voraus! - indexPathsForVisibleItems scheint zurück ein Array von Elementen in der vertikalen Ordnung. Zum Beispiel, wenn Sie 3 Spalten in der UICollectionView, indexPathsForVisibleItems zurück, die sichtbaren Elemente in der ersten Spalte (0, 3, 6). Dann, in der zweiten Spalte (1, 4, 7). Schließlich, die Dritte Spalte (2, 5, 8). Das bedeutet, dass Sie Ihre Bestellung zurückgegeben werden 0, 3, 6, 1, 4, 7, 2, 5, 8 die funktioniert etwas anders als ein tableview zurückgeben würde 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Wenn Sie wirklich brauchen, um das laden der Bilder in der horizontalen Reihenfolge, Sortieren Sie das array zurückgegeben, bevor Sie fortfahren.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie auf iOS 8.0 und höher, die Sie verwenden sollten
collectionView:willDisplayCell:forItemAtIndexPath:
zum Auftakt Ihrer download. Wenn mit iOS 7.0 sollten Sie weiterhin verwendencollectionView:cellForItemAtIndexPath:
.In Ihrem
imageLoaderDidFinishDownloading:
Rückruf, Sie sollten überprüfen, um zu sehen, ob der index-Pfad in Frage ist immer noch sichtbar. Wenn es ist, rufen Sie die entsprechende Zelle und aktualisieren Sie die Ansicht. Wenn die Zelle nicht sichtbar ist, dann ist deine Arbeit getan. Aufruf-reloadData
für jedes Bild Abschluss ist dabei eine Menge von teuren und können wichtige UX-Fragen, wenn Ihr Benutzer ist momentan Mitte scrollen der Tabelle, und Sie setzen Ihren Inhalt. Sie sind auch potenziell tun dieUIImageJPEGRepresentation()
Arbeit viele Male, es würde helfen, Ihr Bildlauf wenn Sie hat diese Arbeit einmal inimageLoaderDidFinishDownloading:
und dann zwischengespeichert, es.Da es aussieht wie der Rückruf geschieht auf einem hintergrund-thread stellen Sie sicher, Sie Bearbeiten Sie nur die
UICollectionView
aus dem Haupt-thread.