带图像的UITableView滚动速度非常慢 [英] UITableView with images scrolls very slowly

查看:67
本文介绍了带图像的UITableView滚动速度非常慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


可能重复:

带图像的表视图,慢速加载和滚动

我有一个 UITableView 从服务器下载 UITableViewCells 的图像。
我观察到tableView滚动速度非常慢。

I have a UITableView which downloads images for the UITableViewCells from a server. I observed that the tableView scrolls very slowly.

我认为这可能会下载问题,但我已经意识到下载完成后表仍然会慢慢滚动并且图片图标尺寸较小。

I thought that this might downloading problem, but I have realized that the table still scrolls slowly after download has finished and the image icon size is less.

我搜索了Google但找不到任何帮助。

I searched Google but couldn't find any help.

- (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;
}


推荐答案

虽然我原来的回答,如下,试图解决与异步图像检索相关的几个关键问题,它仍然是根本上有限的。正确的实现还将确保如果您快速滚动,可见单元格优先于已滚动屏幕的单元格。它还支持取消先前的请求(并在适当的时候取消它们)。

While my original answer, below, attempted to solve several key problems associated with asynchronous image retrieval, it is still fundamentally limited. A proper implementation would also make sure that if you scrolled quickly, that the visible cells were prioritized over cells that had scrolled off screen. It would also support cancelation of prior requests (and canceled them for you when appropriate).

虽然我们可以将这些功能添加到下面的代码中,但它可能更好采用已建立的,经过验证的解决方案,该解决方案利用下面讨论的 NSOperationQueue NSCache 技术,但也解决了上述问题。最简单的解决方案是采用支持异步图像检索的已建立的 UIImageView 类别之一。 AFNetworking SDWebImage 库都有 UIImageView 类别,可以优雅地处理所有这些问题。

While we could add these sorts of capabilities to the below code, it is probably better to adopt an established, proven solution that utilizes the NSOperationQueue and NSCache technologies discussed below, but also addresses the above issues. The easiest solution is to adopt one of the established UIImageView categories that supports asynchronous image retrieval. The AFNetworking and SDWebImage libraries both have UIImageView categories that gracefully handle all of these issues.

您可以使用 NSOperationQueue GCD 进行延迟加载(参见并发编程指南,用于讨论不同的异步操作技术)。前者的优势在于您可以准确指定允许的并发操作数量,这对于从Web加载图像非常重要,因为许多Web服务器限制了它们从给定客户端接受的并发请求数量。

You can use NSOperationQueue or GCD to do your lazy loading (see Concurrency Programming Guide for discussion of different asynchronous operations technologies). The former enjoys an advantage that you can specify precisely how many concurrent operations are permissible, which is very important in loading images from the web because many web servers limit how many concurrent requests they will accept from a given client.

基本思路是:


  1. 在单独的后台队列中提交图像数据的请求;

  2. 完成下载图像后,将UI更新发送回主队列,因为您不应该在后台进行UI更新;

  3. 运行调度的最终UI更新代码时在主队列上,确保 UITableViewCell 仍然可见,并且它尚未出列并重复使用,因为有问题的单元格在屏幕上滚动。如果你不这样做,可能会暂时出现错误的图像。

  1. Submit request of the image data in a separate background queue;
  2. When done downloading image, dispatch UI update back to main queue because you should never do UI updates in the background;
  3. When running dispatched final UI update code on main queue, make sure the UITableViewCell is still visible and that it hasn't been dequeued and reused because the cell in question scrolled off the screen. If you don't do that, the wrong image may momentarily show up.

您可能希望用以下代码替换您的代码:

You would want to replace your code with something like the following code:

首先,为 <定义属性您将用于下载图片的code> NSOperationQueue ,以及 NSCache 用于存储这些图像:

First, define a property for your NSOperationQueue that you will use for downloading images, as well as a NSCache for storing those images:

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

其次,初始化此队列并缓存在 viewDidLoad

Second, initialize this queue and cache in viewDidLoad:

- (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 可能如下所示:

Third, your cellForRowAtIndexPath might look like:

- (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;
}

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

Fourth, and finally, while one might be inclined to write code to purge the cache in low memory situations, it turns out that it does this automatically, so no extra handling is needed here. If you manually simulate a low memory situation in the simulator, you won't see it evict its objects because NSCache doesn't respond UIApplicationDidReceiveMemoryWarningNotification, but during actual operation, when memory is low, the cache will be purged. Actually, NSCache no longer gracefully responds to low memory situations itself, so you really should add observer for this notification and empty the cache in low memory situations.

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

I might suggest a bunch of other optimizations (e.g. perhaps also caching images into persistent storage to streamline future operations; I actually put all of this logic in my own AsyncImage class), but first see if this solves the basic performance issue.

这篇关于带图像的UITableView滚动速度非常慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆