Ios 下载和保存与completionBlock异步的文件时错误的CollectionView单元格图像
我知道已经有很多这样的问题了,但似乎没有一个能解决这个问题 CollectionView从Ios 下载和保存与completionBlock异步的文件时错误的CollectionView单元格图像,ios,objective-c,uicollectionview,uicollectionviewcell,Ios,Objective C,Uicollectionview,Uicollectionviewcell,我知道已经有很多这样的问题了,但似乎没有一个能解决这个问题 CollectionView从文件数组中读取文件名,然后从文档目录加载图像或下载图像,显示并保存到文档中。工作非常顺利,没有任何额外的库,但当滚动快速几个细胞收到错误的图像。通过任何操作调用[collectionView reloadData],或向后滚动将这些错误的图像重新加载到正确的图像 我想这与异步映像分配的单元重用有关,但是如何解决这个问题呢?故事板中定义的CollectionView和自定义单元格。图像存储在基本身份验证之后的
文件
数组中读取文件名,然后从文档目录加载图像或下载图像,显示并保存到文档中。工作非常顺利,没有任何额外的库,但当滚动快速几个细胞收到错误的图像。通过任何操作调用[collectionView reloadData]
,或向后滚动将这些错误的图像重新加载到正确的图像
我想这与异步映像分配的单元重用有关,但是如何解决这个问题呢?故事板中定义的CollectionView和自定义单元格。图像存储在基本身份验证之后的服务器上,所以大多数重用解决方案不适用
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
BrowseCollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
NSString *fileName = [files objectAtIndex:indexPath.item];
NSString *filePath = [thumbDir stringByAppendingPathComponent:fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
cell.imageView.image = [UIImage imageWithContentsOfFile:filePath];
}
else //DOWNLOAD
{
NSString *strURL = [NSString stringWithFormat:@"%@%@", THUMBURL, fileName];
NSURL *fileURL = [NSURL URLWithString:strURL];
cell.imageView.image = [UIImage imageNamed:@"placeholder.jpg"];
[self downloadFromURL:fileURL to:filePath
completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded)
{
cell.imageView.image = image;
}
}];
}
}
cell.imageName = fileName;
return cell;
}
- (void)downloadFromURL:(NSURL*)url to:(NSString *)filePath completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
NSString *authStr = [NSString stringWithFormat:@"%@:%@", LOGIN, PASS];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [authData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:30];
[request setValue:[NSString stringWithFormat:@"Basic %@", authValue] forHTTPHeaderField:@"Authorization"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error)
{
UIImage *image = [[UIImage alloc] initWithData:data];
[data writeToFile:filePath atomically:NO];
completionBlock(YES, image);
} else {
completionBlock(NO, nil);
}
}];
}
我尝试了许多修改,但似乎没有一个能解决问题。目前,其效果是,在快速滚动过程中出现的新单元格被更改了不止一次,其中一些单元格总是以错误的图像结束,这表明存在一些重用问题
提前感谢您的帮助和建议。您需要在某个地方保留一些上下文信息。换句话说,在完成块中,检查单元格是否真的包含下载的图像。如果没有,请不要分配它。例如图像URL。你可以这样做
- 将属性
添加到单元格对象NSURL*imageURL
- 在下载单元格的
将属性之前,请从URL:…
存储到文件URL
图像URL
- 在完成块中,比较
是否匹配fileURL
,如果匹配,则设置图像,否则不设置图像cell.imageURL
- 和
在nil
prepareforeuse
。。。或者,您可以将加载代码移动到cell类中,并在其中仅分配图像的URL等。执行此操作的方法有很多。问题是,当快速滚动时,异步请求完成时,该单元格可能会退出队列并重新用于另一个单元格,从而更新错误的单元格。因此,在完成块中,应确保单元格仍然可见:
[self downloadFromURL:fileURL to:filePath completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
BrowseCollectionViewCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
if (updateCell) { // if the cell is still visible ...
updateCell.imageView.image = image; // ... then update its image
}
}
}];
注意,此UICollectionView
方法cellForItemAtIndexPath
不应与同名的UICollectionViewDataSource
方法collectionView:cellForItemAtIndexPath:
混淆。如果单元格仍然可见,则UICollectionView
方法cellForItemAtIndexPath
返回UICollectionViewCell
,如果不可见,则返回nil
顺便说一下,上面假定此单元格的nsindepath
不能更改(即,在异步检索图像时,不可能在此行上方插入其他行)。这有时不是一个有效的假设。因此,如果您想小心,您真正应该做的是返回到模型并重新计算此模型对象的nsindepath
,并在确定适当的updateCell
引用时使用它
理想情况下,一旦解决了上述问题,您可以对此流程进行一些优化:
sendAsynchronousRequest
。您必须使用基于委托的NSURLConnection
或NSURLSessionTask
,这是可取消的
NSCache
来实现此目的
这需要改变很多。如果您需要帮助,请告诉我们。但在or中使用
UIImageView
类别要容易得多,它可以为您完成所有这些工作。您需要获取块替换中单元格的引用:
[self downloadFromURL:fileURL to:filePath
completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded)
{
cell.imageView.image = image;
}
}];
与
非常好,谢谢!我使用imageName属性进行比较,它解决了这个问题。或者使用@Rob way。并添加他建议的内容(取消、缓存等)。@robertvojta我看到AFNetworking检查相同的URL,而SDWebImage并没有节省我一周(一天半)的时间,因为cellForItemAtIndexPath对(非常)可见的单元格返回零。我希望这个答案很快就会被关闭,以避免无休止的感谢;-鼃)()()()()()())彼此彼此。非常感谢你!谢谢你,这很有效!公元1年。我在NSURLConnection中遇到了一些问题-我认为是在仍然滚动时更新单元格图像的问题。公元2年。我会调查的,谢谢。@yosh很好。关于“ad 1”,我不是建议使用基于委托的
NSURLConnection
来解决您最初的问题,而是提出一个完全无关的观察结果,即它将启用可取消的请求,如果您取消已滚动出视图的单元格的挂起请求,如果你在缓慢的互联网连接下快速滚动,将会大大提高性能。抱歉,如果这是一个自发的观察是一个混乱的来源!没必要道歉。我相信我理解,只是不确定如何实现同样满足上述功能的NSURLConnection,因为我有一些专业知识
[self downloadFromURL:fileURL to:filePath
completionBlock:^(BOOL succeeded, UIImage *image) {
BrowseCollectionViewCell *innerCell = (BrowseCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
if (succeeded)
{
innerCell.imageView.image = image;
}
}];