UICollectionView - загрузка изображений и эффективная перезагрузка данных, таких как Instagram

Я заметил, что приложения, такие как Intagram, используют UICollectionViews для отображения фида фотографий. Я также заметил, что ячейки для этих фотографий каким-то образом размещены на экране до, фактические фотографии загружаются полностью. Затем, когда загрузка завершается, эта фотография хорошо отображается в правильной ячейке.

Я хотел бы скопировать эту функциональность, но я не знаю, как это сделать.

В настоящее время я загружаю большой объект JSON, который я преобразовываю в массив NSdictionaries. Каждый NSDictionary содержит информацию о каждой фотографии, и среди этой информации представлен URL-адрес. На этом URL-адресе я могу найти соответствующее изображение, которое мне нужно загрузить и отобразить в моем UICollectionViewCells

Как и сейчас, я повторяю этот список и запускаю загрузку для каждого URL, который я вижу. Когда эта загрузка будет завершена, я перезагружу коллекцию с помощью [self.collectionView reloadData]. Но, как вы можете себе представить, если у меня есть 30 ячеек, которые все хотят изображения, существует много вызовов reloadData.

Я использую AFNetworking для обработки загрузки, вот метод, который я вызываю на основе URL, о котором я упоминал ранее:

-(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];
}

Итак,, мне интересно, как я могу добиться того, что Instagram и подобные приложения имеют дело с отображением фида с изображениями. Кроме того, я хотел бы знать хороший способ инициировать загрузку для каждой ячейки, и когда эта загрузка будет завершена, обновите эту ячейку, а не перерисуйте весь вид, используя [reloadData]

Спасибо

Ответы

Ответ 1

Техника, которую вы хотите реализовать, называется ленивой загрузкой. Поскольку вы используете AFNetworking, это будет проще реализовать в вашем случае. Каждой ячейке просмотра коллекции требуется UIImageView для отображения изображения. Используйте категорию UIImageView+AFNetworking.h и установите правильный URL изображения, вызывая метод

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

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

    // ...
    return cell;
}

Заполнитель - это изображение, которое будет отображаться до загрузки необходимого изображения. Это просто сделает вам необходимую работу.

Примечание. По умолчанию URL-запросы имеют политику кэширования NSURLCacheStorageAllowed и интервал тайм-аута 30 секунд, и они не обрабатывают файлы cookie. Чтобы настроить URL-адреса по-разному, используйте setImageWithURLRequest:placeholderImage:success:failure:.

Кроме того, для справки, если вы хотите реализовать ленивую загрузку изображений самостоятельно, следуйте этому образцу кода Apple. Это для UITableView, но тот же метод можно использовать и для UICollectionView.

Надеюсь, что это поможет!

Ответ 2

использование

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

вместо

[self.collectionView reloadData];

здесь indexPath - это объект NSIndexPath для соответствующего объекта UICollectionViewCell Object

Ответ 3

Использовать следующее для загрузки изображения Async в ячейке в cellForItemAtIndexPath метод делегата

let url = URL(string: productModel.productImage)

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

Использовать протокол "UICollectionViewDataSourcePrefetching" в вашем ViewController как

класс ViewController: UIViewController, UICollectionViewDataSourcePrefetching {

Установите следующие делегаты в виде коллекции в раскадровке (см. прикрепленное изображение) или программно

В режиме ViewController ViewDidLoad

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