【问题标题】:UITableViewCell load images and reused cellsUITableViewCell 加载图像和重用单元格
【发布时间】:2012-02-08 02:47:24
【问题描述】:

我需要从网络/文件加载一些 UIImages。我正在搜索,我在其他question 中找到了这段代码:

    if (![[NSFileManager defaultManager] fileExistsAtPath:user.image]) {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
        dispatch_async(queue, ^{
            NSData *imageData =[NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]];

            [imageData writeToFile:user.image atomically:YES];
            dispatch_sync(dispatch_get_main_queue(), ^{
                UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
                UIImage *image = [UIImage imageWithData:imageData];
                [self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
                cell.imageView.image = image;
                [cell setNeedsLayout];
                NSLog(@"Download %@",user.image);
            });
        });
        cell.imageView.image=[UIImage imageNamed:@"xger86x.jpg"];
    } else {
        NSLog(@"cache");
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
        dispatch_async(queue, ^{
            UIImage *image = [UIImage imageWithContentsOfFile:user.image];
            //[self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
            dispatch_sync(dispatch_get_main_queue(), ^{
                UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
                newCell.imageView.image=image;
                [newCell setNeedsLayout];
            });
        });
    }

但问题是,当我快速滚动到底部或顶部时,图像加载错误并且结束时会有短暂的延迟。

所以问题是.. 当我使用队列来获取 UIImages 时,如何将它们加载到正确的单元格中?谢谢!

【问题讨论】:

    标签: ios uitableview


    【解决方案1】:

    我怀疑您看到的错误图像是由于您没有设置占位符图像,以防您拥有图像的本地副本但仍异步检索本地副本。此外,在您附加的用于加载本地副本的代码中,您在后台线程上使用UIImage 一个UIKit 组件。

    另外有趣的是,您似乎正在做某种UIImage 缓存。将图像添加到我假设的是一个名为imageFriendsNSMutableArray 属性。但是,如果您有文件的本地副本,您似乎已经注释掉了缓存添加。此外,您发布的代码从不使用缓存的UIImages

    虽然 2 级缓存似乎有点过火,但如果您想这样做,您可以这样做:

    UIImage *userImage = [self.imageFriends objectForKey:[NSNumber numberWithInt:user.userId]];
    if (userImage) { // if the dictionary of images has it just display it
        cell.imageView.image = userImage;
    }
    else {
        cell.imageView.image = [UIImage imageNamed:@"xger86x.jpg"]; // set placeholder image
        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:user.image];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSData *imageData = nil;
            if (fileExists){
                imageData = [NSData dataWithContentsOfFile:user.image];
            }
            else {
                imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]];
                [imageData writeToFile:user.image atomically:YES];
            }
            if (imageData){
               dispatch_async(dispatch_get_main_queue(), ^{ 
                        // UIKit, which includes UIImage warns about not being thread safe
                        // So we switch to main thread to instantiate image
                   UIImage *image = [UIImage imageWithData:imageData];
                   [self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
                   UITableViewCell *lookedUpCell = [tableView cellForRowAtIndexPath:indexPath];
                   if (lookedUpCell){
                       lookedUpCell.imageView.image = image;
                       [lookedUpCell setNeedsLayout];
                   }
               }); 
            }
        });
    }
    

    UIImages 是UIKit 的一部分,不是线程安全的。但是你可以在另一个线程上加载NSData

    【讨论】:

    • 关于 UIImage 不是线程安全的注释是我试图解决的问题的关键。
    • 如果单元格已被重复使用,此解决方案如何工作?
    【解决方案2】:

    您正在异步加载图像,因此在快速滚动期间,单元格可以更快地重用,然后下载图像。避免加载错误图像的一种方法是检查加载图像时单元格是否已被重用。或者在您将新单元格出列时取消所有正在进行的请求。

    我还建议查看AFNetworking,因为它包含UIImageView 的有用类别,因此您可以执行以下操作:

    [imageView setImageWithURL:[NSURL URLWithString:@"http://i.imgur.com/r4uwx.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]];
    

    它还包含cancelImageRequestOperation 方法,用于取消正在进行的请求。然后您的代码将如下所示:

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    } else {
        [cell.imageView cancelImageRequestOperation];
    }
    [cell.imageView setImageWithURL:[NSURL URLWithString:user.imageURL] placeholderImage:[UIImage imageNamed:@"xger86x.jpg"]]; 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-06
      • 1970-01-01
      相关资源
      最近更新 更多