UITableView与图像滚动很慢

可能重复:
带有图像的表格视图,缓慢加载和滚动

我有一个UITableView从服务器下载UITableView图像。 我观察到tableView滚动很慢。

我以为这可能会下载问题,但我已经意识到,下载完成后,表仍然滚动,图像图标大小较小。

我search了Google,但找不到任何帮助。

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]]; NSString *Path; Path = [NSString stringWithFormat:@"http://%@",[test.arrImages objectAtIndex:indexPath.row]]; NSLog(@"image-->%@",[test.arrImages objectAtIndex:indexPath.row]); NSString *strImage = Path; NSURL *url4Image = [NSURL URLWithString:strImage]; NSData *data = [NSData dataWithContentsOfURL:url4Image]; image =[[UIImage alloc] initWithData:data]; cell.imageView.image =image; [image release]; return cell; } 

尽pipe下面我的原始答案试图解决与asynchronous图像检索相关的几个关键问题,但它仍然受到了根本性的限制。 一个适当的实施也将确保如果你快速滚动,可见细胞优先于在屏幕上滚动的细胞。 它也支持取消之前的请求(并在适当的时候为你取消)。

虽然我们可以将这些function添加到下面的代码中,但是最好采用已经build立的,经过validation的解决scheme,该解决scheme使用下面讨论的NSOperationQueueNSCache技术,但也解决了上述问题。 最简单的解决scheme是采用支持asynchronous图像检索的已build立的UIImageView类别之一。 AFNetworking和SDWebImage库都有UIImageView类别,可以正常处理所有这些问题。


您可以使用NSOperationQueue或GCD进行延迟加载(请参阅“ 并发编程指南”以讨论不同的asynchronous操作技术)。 前者具有的优点是可以精确地指定允许多less个并发操作,这在从Web加载图像时非常重要,因为许多Web服务器限制了从给定客户端接受多less个并发请求。

基本的想法是:

  1. 在一个单独的背景队列中提交图像数据的请求;
  2. 完成下载图像后,将UI更新调回主队列,因为您不应该在后台执行UI更新;
  3. 在主队列上运行调度的最终用户界面更新代码时,请确保UITableViewCell仍然可见,并且它没有出列并重用,因为有问题的单元格滚动出屏幕。 如果你不这样做,错误的形象可能会瞬间显现出来。

你会想用下面的代码replace你的代码:

首先,为您的NSOperationQueue定义一个属性,您将使用它来下载图像,以及一个用于存储这些图像的NSCache

 @property (nonatomic, strong) NSOperationQueue *imageDownloadingQueue; @property (nonatomic, strong) NSCache *imageCache; 

其次,在viewDidLoad初始化这个队列和caching:

 - (void)viewDidLoad { [super viewDidLoad]; self.imageDownloadingQueue = [[NSOperationQueue alloc] init]; self.imageDownloadingQueue.maxConcurrentOperationCount = 4; // many servers limit how many concurrent requests they'll accept from a device, so make sure to set this accordingly self.imageCache = [[NSCache alloc] init]; // the rest of your viewDidLoad } 

第三,你的cellForRowAtIndexPath可能看起来像这样:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]]; // code change starts here ... initialize image and then do image loading in background NSString *imageUrlString = [NSString stringWithFormat:@"http://%@", [test.arrImages objectAtIndex:indexPath.row]]; UIImage *cachedImage = [self.imageCache objectForKey:imageUrlString]; if (cachedImage) { cell.imageView.image = cachedImage; } else { // you'll want to initialize the image with some blank image as a placeholder cell.imageView.image = [UIImage imageNamed:@"blankthumbnail.png"]; // now download in the image in the background [self.imageDownloadingQueue addOperationWithBlock:^{ NSURL *imageUrl = [NSURL URLWithString:imageUrlString]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; UIImage *image = nil; if (imageData) image = [UIImage imageWithData:imageData]; if (image) { // add the image to your cache [self.imageCache setObject:image forKey:imageUrlString]; // finally, update the user interface in the main queue [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Make sure the cell is still visible // Note, by using the same `indexPath`, this makes a fundamental // assumption that you did not insert any rows in the intervening // time. If this is not a valid assumption, make sure you go back // to your model to identify the correct `indexPath`/`updateCell` UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; if (updateCell) updateCell.imageView.image = image; }]; } }]; } return cell; } 

第四,最后,虽然人们可能倾向于编写代码来清除低内存情况下的caching,但事实certificate,它会自动执行此操作,因此不需要额外的处理。 如果你在模拟器中手动模拟一个低内存情况,你不会看到它驱逐它的对象,因为NSCache没有响应UIApplicationDidReceiveMemoryWarningNotification ,但是在实际操作中,当内存不足时,caching将被清除。 实际上, NSCache本身不再优雅地响应低内存情况,因此您应该为此通知添加观察者并在低内存情况下清空caching。

我可能会提出一些其他优化(例如,也许也将图像caching到持久性存储以简化未来的操作;实际上我把这些逻辑放在我自己的AsyncImage类中),但首先看看是否解决了基本的性能问题。

写在你的UITableView cellForRowAtIndex:方法

 asyncImageView = [[AsyncImageView alloc]initWithFrame:CGRectMake(30,32,100, 100)]; [asyncImageView loadImageFromURL:[NSURL URLWithString:your url]]; [cell addSubview:asyncImageView]; [asyncImageView release]; 

需要导入AsyncImageView class并为AsyncImageView class创build对象

如前所述:不要在cellForRowAtIndexPath中做任何繁重的工作,您可以使用GCD轻松解决。 使用块从后台线程加载图像

你应该看看使用NSOperationQueue来处理延迟加载的图像和自定义tableviewcell。 你可以在这里获得NSOperationQueue示例

谷歌的tweetie自定义tableviewcell这应该设置你在正确的方向。

苹果有一个示例项目,用于下载tableViews中的图像: LazyTableImages

build议您将图像存储在数组中,并将其填充到viewDidLoad ,然后放入cellForRowAtIndexPath:只需设置

 cell.imageView.image = [yourImageArray objectAtIndex:indexPath.row]; 

就缓慢而言,这是因为你阻止主线程在你的cellForRowAtIndexPath方法中下载URLDATA,所以当滚动,除非和直到图像不被取出你的应用程序运行的主线程将被阻止。

滚动速度非常慢,因为您正在主线程中加载图像,即同步。 你可以在后台线程中同步执行,看看SDWebImage 。