我下载的图片为我的UITableView异步使用GCD,但有一个问题 - 当滚动图像闪烁并改变所有的时间。 我试着用每一个细胞图像设置为零,但它并没有太大的帮助。 当滚动备份速度快,所有图像都是错误的。 我能做些什么? 这里是我的细胞的方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (self.loader.parsedData[indexPath.row] != nil)
{
cell.imageView.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.loader.parsedData[indexPath.row] objectForKey:@"imageLR"]]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
[cell setNeedsLayout];
});
});
cell.textLabel.text = [self.loader.parsedData[indexPath.row] objectForKey:@"id"];
}
return cell;
}
Answer 1:
这里的问题是,你的图像获取块都抱着到的tableview细胞引用。 当下载完成时,它设置imageView.image
财产,即使你已经循环显示不同行的单元格。
你将需要你下载完成块测试图像是否仍然设置图像之前,相关的细胞。
另外值得一提的是,你没有任何地方存储比小区之外的图像,所以你会再次每次滚动屏幕上一行的时间内下载它们。 你可能想不想找个它们进行缓存并开始下载前对本地缓存图像。
编辑:这里有一个简单的方法来测试,使用电池的tag
属性:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.tag = indexPath.row;
NSDictionary *parsedData = self.loader.parsedData[indexPath.row];
if (parsedData)
{
cell.imageView.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:parsedData[@"imageLR"]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
cell.imageView.image = image;
[cell setNeedsLayout];
}
});
}
});
cell.textLabel.text = parsedData[@"id"];
}
return cell;
}
Answer 2:
问题的关键是,你没有完全理解细胞再利用的理念。 这并不与异步下载吻合得很好。
块
^{
cell.imageView.image = image;
[cell setNeedsLayout];
}
得到当请求完成后,所有数据被加载执行。 但是,在创建块时电池获取其值。
通过时执行该块时细胞仍然指向现有小区中的一个。 但它很可能使用户继续滚动。 将细胞对象是在此期间再利用和图像与被重复使用,分配和显示的“ 旧 ”细胞相关联。 之后不久,该正确的图像被加载并分配和显示,除非用户已经进一步滚动。 等等等等。
你应该寻找这样做的一个更聪明的方式。 有很多turorials的。 谷歌懒图像加载。
Answer 3:
使用索引路径来获取细胞。 如果它是不可见的细胞将是nil
,你会不会有问题。 当然,你可能会想缓存时,它的下载,以便立即设置单元格的形象,当你有像已经是数据。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (self.loader.parsedData[indexPath.row] != nil)
{
cell.imageView.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
// You may want to cache this explicitly instead of reloading every time.
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.loader.parsedData[indexPath.row] objectForKey:@"imageLR"]]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
// Capture the indexPath variable, not the cell variable, and use that
UITableViewCell *blockCell = [tableView cellForRowAtIndexPath:indexPath];
blockCell.imageView.image = image;
[blockCell setNeedsLayout];
});
});
cell.textLabel.text = [self.loader.parsedData[indexPath.row] objectForKey:@"id"];
}
return cell;
}
Answer 4:
我一直在研究这个问题,我发现通过自定义的UITableViewCell的一个很好的办法。
#import <UIKit/UIKit.h>
@interface MyCustomCell : UITableViewCell
@property (nonatomic, strong) NSURLSessionDataTask *imageDownloadTask;
@property (nonatomic, weak) IBOutlet UIImageView *myImageView;
@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *activityIndicator;
@end
现在,在你的TableViewController,申报NSURLSessionConfiguration和NSURLSession两个属性和viewDidLoad中初始化它们:
@interface MyTableViewController ()
@property (nonatomic, strong) NSURLSessionConfiguration *sessionConfig;
@property (nonatomic, strong) NSURLSession *session;
.
.
.
@end
@implementation TimesVC
- (void)viewDidLoad
{
[super viewDidLoad];
_sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:_sessionConfig];
}
.
.
.
让我们supose你的数据源是一个的NSMutableDictionary的阵列(或的NSManagedObjectContext)。 您可以下载easilly图像的每个细胞,与缓存,这样的:
.
.
.
- (MyCustomCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
if (!cell)
{
cell = [[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"cell"];
}
NSMutableDictionary *myDictionary = [_myArrayDataSource objectAtIndex:indexPath.row];
if (cell.imageDownloadTask)
{
[cell.imageDownloadTask cancel];
}
[cell.activityIndicator startAnimating];
cell.myImageView.image = nil;
if (![myDictionary valueForKey:@"image"])
{
NSString *urlString = [myDictionary valueForKey:@"imageURL"];
NSURL *imageURL = [NSURL URLWithString:urlString];
if (imageURL)
{
cell.imageDownloadTask = [_session dataTaskWithURL:imageURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (error)
{
NSLog(@"ERROR: %@", error);
}
else
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 200)
{
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[myDictionary setValue:data forKey:@"image"];
[cell.myImageView setImage:image];
[cell.activityIndicator stopAnimating];
});
}
else
{
NSLog(@"Couldn't load image at URL: %@", imageURL);
NSLog(@"HTTP %d", httpResponse.statusCode);
}
}
}];
[cell.imageDownloadTask resume];
}
}
else
{
[cell.myImageView setImage:[UIImage imageWithData:[myDictionary valueForKey:@"image"]]];
[cell.activityIndicator stopAnimating];
}
return cell;
}
我希望它可以帮助一些开发! 干杯。
鸣谢: 在iOS的7表查看图片
Answer 5:
你有没有想过用SDWebImage的? 它下载从给定的URL异步也形象。 我没有使用整个库(仅进口的UIImageView + WebCache.h)。 导入后,所有你需要做的就是调用该方法,例如:
[UIImageXYZ sd_setImageWithURL:["url you're retrieving from"] placeholderImage:[UIImage imageNamed:@"defaultProfile.jpeg"]];
这可能是矫枉过正,如果你正在使用AFNetworking 2.0,但它的工作为我。
这里是链接到GitHub的,如果你想给它一个尝试
Answer 6:
不要重新发明轮子,只需使用这对夫妻真棒荚:
https://github.com/rs/SDWebImage
https://github.com/JJSaccolo/UIActivityIndicator-for-SDWebImage
就是这么简单:
[self.eventImage setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", [SchemeConfiguration APIEndpoint] , _event.imageUrl]]
placeholderImage:nil
completed:^(UIImage *image,
NSError *error,
SDImageCacheType cacheType,
NSURL *imageURL)
{
event.image = UIImagePNGRepresentation(image);
} usingActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
Answer 7:
除了接受谢默斯·坎贝尔的回答,你也应该知道,这一段时间不工作。 在这种情况下,我们应该重新加载特定的细胞。 所以
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
cell.imageView.image = image;
[cell setNeedsLayout];
}
});
}
应改为,
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
cell.imageView.image = image;
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}
});
}
Answer 8:
对于那些担心指数的路径变化,不是一致的,一个可能的解决方案是子类的UITableViewCell或UICollectionViewCell并添加一个名为像stringTag一个实例变量。 然后,把你的stringTag正在下载照片的URL。 当设置图像,检查stringTag仍然是正确的网址。
看到这个答案的更多详细信息: 异步抓取完成:正在显示的单元格? 。
这里是我的类:
import Foundation
import UIKit
class ImageAsyncCollectionViewCell : UICollectionViewCell {
var stringTag: String?
}
然后使用细胞时:
cell.stringTag = photoKey
cell.imageView.image = self.blankImage
if ImageCache.default.imageCachedType(forKey: photoKey).cached {
ImageCache.default.retrieveImage(forKey: photoKey, options: nil) {
image, cacheType in
if let image = image {
DispatchQueue.main.async {
if cell.stringTag == photoKey {
cell.imageView.image = image
}
}
} else {
print("Doesn't exist in cache, but should")
self.setCellWithPhoto(photoKey: photoKey, cell: cell)
}
}
} else {
self.setCellWithPhoto(photoKey: photoKey, cell: cell)
}
文章来源: Asynchronous downloading of images for UITableView with GCD