UICollectionView - 下载图像并有效地重新加载数据,例如 Instagram

Posted

技术标签:

【中文标题】UICollectionView - 下载图像并有效地重新加载数据,例如 Instagram【英文标题】:UICollectionView - Downloading images and effective reloading of data, like Instagram 【发布时间】:2013-08-27 06:57:43 【问题描述】:

我注意到像 Intagram 这样的应用程序使用UICollectionViews 来显示照片的提要。 我还注意到这些照片的单元格以某种方式放置在屏幕上实际照片完全下载之前。然后,当下载完成时,该照片会很好地显示在正确的单元格中。

我想复制该功能,但我不知道如何去做。

我目前正在下载一个大型 JSON 对象,我将其转换为 NSDictionaries 数组。每个NSDictionary 都包含有关每张照片的信息,并且在这些信息中,会显示一个 URL。在这个网址,我可以在我的UICollectionViewCells找到我需要下载并显示的对应图片

就目前而言,我会迭代此列表并为我看到的每个 URL 启动下载。下载完成后,我使用[self.collectionView reloadData] 重新加载collectionview。但是,您可以想象,如果我有 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 sample code。这适用于UITableView,但同样的技术也可以用于UICollectionView

希望有帮助!

【讨论】:

然而,当我实现这个时,collectionview 在滚动时非常麻烦。这种方法是完全异步的吗?知道如何使我的收藏视图的滚动不那么麻烦吗? =) @Eyeball 是的,这是完全异步的。在cellForItemAtIndexPath 中,您是否还有其他类似 FileIO 的操作?任何会导致主线程明显暂停的操作? 还有一点,您现在不需要为每张图片重新加载集合。下载并分配图像后,AFNetworking 代码负责刷新图像视图。 您可以使用相同的分类功能,首先在AFNetworking缓存中查找,如果找到则使用本地副本,如果没有则下载新副本。如果设备收到内存不足警告,您的应用程序缓存目录将被清除,需要您重新下载和管理缓存。而是让 AFNetworking 高效地完成它:) AFNetworking 也会在缓存时处理资源刷新,例如它会检查 Etag 中的缓存图像。如果图片提供者更改了相同 url 的图片资源,则 AFNetworking 将下载修改后的资源并替换为缓存的 URL。【参考方案2】:

使用

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

而不是

[self.collectionView reloadData];

这里的indexPath 是对应UICollectionViewCell 对象的NSIndexPath 对象

【讨论】:

【参考方案3】:

cellForItemAtIndexPath 委托方法中使用以下方法在单元格中加载图像异步

let url = URL(string: productModel.productImage)

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

在你的 ViewController 中实现协议 “UICollectionViewDataSourcePrefetching”

类 ViewController:UIViewController、UICollectionViewDataSourcePrefetching

在情节提要中将以下代表设置到您的集合视图(参见附图) 或以编程方式

ViewController的 viewDidLoad方法中

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

【讨论】:

不,UICollectionViewDataSourcePrefetching 仅适用于 ios 10+

以上是关于UICollectionView - 下载图像并有效地重新加载数据,例如 Instagram的主要内容,如果未能解决你的问题,请参考以下文章

延迟加载 UICollectionView 图像

如何在使用自定义单元格的 UICollectionView 中正确设置异步图像下载?

UICollectionView在滚动时重新加载错误的图像

UICollectionView 在滚动时重新加载错误的图像

知道 UICollectionView 是不是正在滚动

如何使用 Alamofire 在 UICollectionView 单元格中使用标签显示图像?