带图像的UITableView滚动速度非常慢

时间:2012-10-03 06:52:48

标签: iphone ios uitableview scroll

  

可能重复:
  Table View with Images, slow load and scroll

我有UITableView从服务器下载UITableViewCells的图片。 我发现tableView的滚动速度非常慢。

我认为这可能会下载问题,但我已经意识到下载完成后表格仍然会慢慢滚动,图像图标尺寸会减少。

我搜索了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;
}

6 个答案:

答案 0 :(得分:18)

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

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


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

基本理念是:

  1. 在单独的后台队列中提交图像数据的请求;
  2. 完成下载图像后,将UI更新回主队列,因为您不应该在后台进行UI更新;
  3. 在主队列上运行调度的最终UI更新代码时,请确保UITableViewCell仍然可见并且尚未出列并重复使用,因为有问题的单元格已离开屏幕。如果你不这样做,可能会暂时出现错误的图像。
  4. 您可能希望使用以下代码替换代码:

    首先,定义用于下载图片的NSOperationQueue的属性,以及用于存储这些图片的NSCache

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

    其次,在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可能如下:

    - (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不再优雅地响应低内存情况本身,所以你真的应该为这个通知添加观察者并在低内存情况下清空缓存。

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

答案 1 :(得分:1)

UITableView cellForRowAtIndex:方法

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

需要导入AsyncImageView class并为该类创建对象

答案 2 :(得分:0)

滚动非常慢,因为您正在主线程中加载图像,即同步。您可以在后台线程中执行相同的操作,即异步,查看SDWebImage

答案 3 :(得分:0)

建议您将图像存储在一个数组中,然后将其填入viewDidLoad,然后填入cellForRowAtIndexPath:设置

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

就缓慢而言,这是因为您通过在cellForRowAtIndexPath方法中下载URLDATA来阻止主线程,因此在滚动时除非直到图像未被提取,否则应用程序的主线程运行将被阻止。

答案 4 :(得分:0)

如前所述:不要在cellForRowAtIndexPath中做任何繁重的工作 您可以使用GCD轻松到处走走。 loading images from a background thread using blocks

答案 5 :(得分:0)

您应该使用NSOperationQueue来处理延迟加载图像和自定义tableviewcell。您可以获取NSOperationQueue here

的示例

谷歌推特自定义tableviewcell 这应该让你朝着正确的方向前进。

Apple有一个用于在tableViews中下载图像的示例项目:LazyTableImages