2013-08-27 6 views
14

Ho notato che le app come Intagram utilizza UICollectionViews per visualizzare il feed di foto. Ho anche notato che le celle per queste foto sono in qualche modo posizionate sullo schermo prima delle le foto reali vengono scaricate completamente. Quindi, al termine del download, la foto viene visualizzata correttamente nella cella corretta.UICollectionView - Download di immagini ed efficace ricaricamento dei dati, come Instagram

Vorrei copiare questa funzionalità, ma non so come procedere.

Attualmente sto scaricando un gran JSON oggetto trasformo in un array di NSDictionaries. Ogni NSDictionary contiene informazioni su ciascuna foto e, tra queste informazioni, viene presentato un URL. A questo URL, posso trovare l'immagine corrispondente che ho bisogno di scaricare e visualizzare nel mio UICollectionViewCells

Per ora, io iterare questo elenco e avviare un download per ogni URL che vedo. Al termine del download, ricarico la collectionview utilizzando [self.collectionView reloadData]. Ma, come puoi immaginare, se ho 30 celle che vogliono tutte un'immagine, ci sono molte chiamate reloadData.

Sto usando AFNetworking per gestire il download, qui è il metodo, che ho chiamata in base l'URL ho detto prima:

-(void) downloadFeedImages:(NSString *) photoURL imageDescs:(NSDictionary*)imageDescs photoId:(NSString *)photoID{ 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 
    NSString *directory = [paths objectAtIndex:0]; 

    NSString* foofile = [directory stringByAppendingPathComponent:photoID]; 

    if([[NSFileManager defaultManager] fileExistsAtPath:foofile]){ 
     // IF IMAGE IS CACHED 
     [self.collectionView reloadData]; 
     return; 
    } 

    NSLog(@"photoURL: %@", photoURL); 
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:photoURL]]; 
    AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request 
                       imageProcessingBlock:nil 
                          success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { 
                           // Save Image 
                           NSLog(@"URL-RESPONSE:%@", image); 
                           NSString *myFile = [directory stringByAppendingPathComponent:photoID]; 

                           NSData *imageData = UIImagePNGRepresentation(image); 
                           [imageData writeToFile:myFile atomically: YES]; 
                           [self.collectionView reloadData]; 
                          } 
                          failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { 
                           NSLog(@"ERROR: %@", [error localizedDescription]); 
                          }]; 
    [[WebAPI sharedInstance] enqueueHTTPRequestOperation:operation]; 
} 

Quindi fondamentalmente, mi chiedo come posso ottenere la funzionalità che Instagram e applicazioni simili hanno quando si tratta di visualizzare un feed con immagini. Inoltre, vorrei sapere un buon modo per avviare un download per ogni cella, e quando questo download è terminato, aggiornare quella cella, senza ridisegnare l'intera vista con [reloadData]

Grazie

risposta

17

La tecnica che si desidera implementare si chiama caricamento lento. Dal momento che stai usando AFNetworking sarà più facile implementarlo nel tuo caso. Ciascuna cella della vista raccolta deve avere un UIImageView per visualizzare l'immagine. Utilizzare la categoria UIImageView+AFNetworking.h e impostare l'URL dell'immagine corretta chiamando il metodo

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // .... 

    [cell.imageView setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:@"placeholder.png"]]; 

    // ... 
    return cell; 
} 

segnaposto è l'immagine che verrà visualizzata finché l'immagine desiderata viene scaricato. Questo semplicemente farà il lavoro richiesto per te.

Nota: per impostazione predefinita, le richieste di URL ha una politica di cache di NSURLCacheStorageAllowed e un intervallo di timeout di 30 secondi, e sono impostati non gestisce i cookie. Per configurare le richieste URL in modo diverso, utilizzare setImageWithURLRequest:placeholderImage:success:failure:.

Inoltre, per riferimento, se si desidera implementare il caricamento lazy delle immagini da soli, seguire questo Apple sample code. Questo è per UITableView ma la stessa tecnica può essere utilizzata anche per UICollectionView.

Spero che questo aiuti!

+0

Tuttavia, quando implemento questo, il CollectionView è abbastanza hacky durante lo scorrimento. questo metodo è completamente asincrono? Qualche idea su come rendere lo scorrimento della mia collezione meno hacky? =) – Eyeball

+1

@Eyeball Sì, questo è completamente asincrono. Ci sono altre operazioni che stai eseguendo come FileIO in 'cellForItemAtIndexPath'? Qualsiasi operazione che causerebbe una pausa notevole nel thread principale? – Amar

+0

Un'altra cosa, non è necessario ricaricare la raccolta per ogni immagine ora. Il codice AFNetworking si occupa di aggiornare la vista dell'immagine una volta che l'immagine è stata scaricata e assegnata ad essa. – Amar

2

uso

[self.collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];

anziché

[self.collectionView reloadData];

qui indexPath è l'oggetto NSIndexPath per la corrispondente UICollectionViewCell oggetto

0

utilizzare seguente per caricare immagine asincrono nella cella in ce llForItemAtIndexPath metodo delegato

let url = URL(string: productModel.productImage) 

    let data = try? Data(contentsOf: url!) 
    DispatchQueue.main.async(execute: { 
     collectionViewCell.imageView.image = UIImage(data: data!) 
    }); 

Implementare protocollo "UICollectionViewDataSourcePrefetching" in voi ViewController come

classe ViewController: UIViewController, UICollectionViewDataSourcePrefetching {

Set seguente delegati alla vista collezione storyboard (vedi l'immagine allegata) o programmaticamente

In viewDidLoad metodo ViewController

collectionView.delegate = self 
collectionView.dataSource = self 
collectionView.prefetchDataSource = self