UITableView中的图像在滚动时不断重新加载和错误的图像闪烁

我已经创build了一个UITableView从URL请求填充每个单元格。 我已经使用'dispatch_queue'来防止UItableView冻结。 由于某种原因,当我滚动UITableView图像闪烁和消失,填补了错误的细胞一秒钟,直到修复自己。 这是我的代码。 我正在使用restkit提取Feed

customCell.customCellTextLabel.text = [NSString stringWithFormat:@"%@",feedO.title]; NSString *string = [NSString stringWithFormat:@"%@",feedO.body]; NSURL *urlString; NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil]; NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])]; for (NSTextCheckingResult *match in matches) { if ([match resultType] == NSTextCheckingTypeLink) { urlString = [match URL]; NSLog(@"found Body URL: %@ and title %@", urlString,feedO.title); } } dispatch_queue_t imageQueue = dispatch_queue_create("imageDownloader",nil); dispatch_async(imageQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:urlString]; dispatch_async(dispatch_get_main_queue(), ^{ customCell.customCellImageView.image = [UIImage imageWithData: data]; }); }); return customCell; 

你应该考虑使用这个库SDWebImage,在这里https://github.com/rs/SDWebImage提供这样的问题。 它非常容易地处理远程图像的asynchronous下载和caching。

最简单的安装是通过使用CocoaPods完成的

CocoaPods( http://cocoapods.org )是Objective-C的依赖pipe理器,可以自动化和简化在项目中使用第三方库的过程。 有关更多详细信息,请参阅入门部分。

在您的Podfile中使用

 platform :ios, '6.1' pod 'SDWebImage', '~>3.6' 

在安装SDWebImage的依赖关系之后,只需在视图控制器中使用以下几行:

 #import <SDWebImage/UIImageView+WebCache.h> ... - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *MyIdentifier = @"MyIdentifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease]; } // Here we use the new provided setImageWithURL: method to load the web image [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]]; cell.textLabel.text = @"My Text"; return cell; } 

你的代码缺less一行,我补充说。

customCell.customCellImageView.image = nil;

 customCell.customCellTextLabel.text = [NSString stringWithFormat:@"%@",feedO.title]; NSString *string = [NSString stringWithFormat:@"%@",feedO.body]; NSURL *urlString; customCell.customCellImageView.image =nil; NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil]; NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])]; for (NSTextCheckingResult *match in matches) { if ([match resultType] == NSTextCheckingTypeLink) { urlString = [match URL]; NSLog(@"found Body URL: %@ and title %@", urlString,feedO.title); } } dispatch_queue_t imageQueue = dispatch_queue_create("imageDownloader",nil); dispatch_async(imageQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:urlString]; dispatch_async(dispatch_get_main_queue(), ^{ customCell.customCellImageView.image = [UIImage imageWithData: data]; }); }); return customCell; 

原因是UITableView正在重用所创build的单元格。 所以首先它创build让你的CustomCell类10。

单元格0
单元格1

小区9

通过创build这10个单元格,它将启动10个asynchronous调用来获取图像。

块0 – >单元格0
块1 – >单元格1

块9→小区9

如果在所有10个下载完成之前不滚动,这将工作正常。

但是一旦你开始滚动, UITableView将开始重用所创build的单元格并启动新的下载。

所以首先它可以重用Cell 0并创build块10 – > Cell 0。

如果在单元0被重新利用时块0没有完成,现在你将有两个块想把它们的图像放到同一个单元上。 导致以下两种情况:

  1. 块0首先完成,然后块10完成
  2. 方块10先完成,然后方块0完成

这是什么导致“闪烁”。

然后想象在几秒钟内滚动1000个单元格:)

您需要能够取消正在重复使用的单元的排队模块。

我会使用例如SDWebImage或FastImageCache 。

你怎么说的。 你在另一个线程中捕捉图像,以防止冻结。 在tableView中,单元格被重用,而在一个后台线程中,您正在捕捉新的图片,在主线程中,将返回具有上一张图片的单元格。 在这里的代码我修复它和更好的解释:

customCell.customCellTextLabel.text = [NSString stringWithFormat:@“%@”,feedO.title];

 NSString *string = [NSString stringWithFormat:@"%@",feedO.body]; NSURL *urlString; NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil]; NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])]; for (NSTextCheckingResult *match in matches) { if ([match resultType] == NSTextCheckingTypeLink) { urlString = [match URL]; NSLog(@"found Body URL: %@ and title %@", urlString,feedO.title); } } // Here you need, at least quit the previous imagen, is better if you implement // and activity indicator (here start), and also you should implement some imagen cache. customCell.customCellImageView.image = nil; // Apart from here, go to another thread. dispatch_queue_t imageQueue = dispatch_queue_create("imageDownloader",nil); dispatch_async(imageQueue, ^{ // this operation is slowly that return a cell, this happens after [1] NSData *data = [[NSData alloc] initWithContentsOfURL:urlString]; dispatch_async(dispatch_get_main_queue(), ^{ //If you implement and activity indicator stop here. customCell.customCellImageView.image = [UIImage imageWithData: data]; }); }); // [1] the cell is returned before the image is ready. return customCell; 

以上答案是正确的。 但是不需要在单元格中下载图像。 你可以在table loading载入之前在viewWillAppear或者ViewDidLoad中下载镜像,以满足你的需求。

每当您的图像将再次下载时,滚动您的表格。 为了防止这种情况,我更喜欢你从caching中获取图像,并直接在你的图像视图中设置。 根据你的代码。 如果从0索引滚动到10,则单元格将下载全部10个单元格的图像。 之后,如果你再次滚动10索引0。 然后图像将再次下载。

你可以使用( https://github.com/rs/SDWebImage )下载图片asynchronous。 它非常容易和快速。

我首选这个库,因为它会处理你的caching。 只需写下面的代码

 #import "UIImageView+WebCache.h" // in cellForRowAtIndexPath. [customCell.customCellImageView sd_setImageWithURL: [NSURL URLWithString:urlString]]; 

删除下面的代码。

 dispatch_queue_t imageQueue = dispatch_queue_create("imageDownloader",nil); dispatch_async(imageQueue, ^{ NSData *data = [[NSData alloc] initWithContentsOfURL:urlString]; dispatch_async(dispatch_get_main_queue(), ^{ customCell.customCellImageView.image = [UIImage imageWithData: data]; }); }); 

也许这会帮助你。