【问题标题】:Images in UITableView keep re-loading and wrong images flash while scrollingUITableView 中的图像在滚动时不断重新加载并且错误的图像闪烁
【发布时间】:2014-11-06 09:07:11
【问题描述】:

我创建了一个 UITableView,它根据 URL 请求填充每个单元格。我使用“dispatch_queue”来防止 UItableView 冻结。出于某种原因,当我滚动浏览 UITableView 时,图像会闪烁并消失并填充错误的单元格一秒钟,直到自行修复。这是我的代码。我正在使用 restkit 提取提要

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;

【问题讨论】:

  • 当您每次滚动时,您的单元格将再次下载图像。我更喜欢使用 (github.com/rs/SDWebImage)。这是一个很好的库,可以异步下载图像并且它还维护缓存。因为根据您的代码。如果您的单元格下载索引 0to5 的图像。当您再次返回索引 0to5 时,您的图像将再次下载。

标签: ios objective-c xcode uitableview ios8


【解决方案1】:

您应该考虑使用这个库 SDWebImage,可在此处https://github.com/rs/SDWebImage 找到此类问题。它非常容易处理远程图像的异步下载和缓存。

使用 CocoaPods 完成最简单的安装

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

在你的 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;
}

【讨论】:

  • 我以前试过这个,但我没有明确需要将 SDWebImage 中的哪些文件添加到我的项目中?我只是将名为“SDWebImage”的文件夹添加到我的项目中吗?
  • 最简单的方法是在你的项目中使用 Pods (cocoapods.org) 来管理第三方库依赖。只需在 PodFile 中向完整的 SDWebImage 项目添加依赖项,然后使用我在答案中提供的代码
  • 非常感谢您的帮助,我用过可可豆荚,我用它来为我的项目安装“RestKit”,但不知道如何安装 SDWebImage
  • 只需在您的 PodFile 中添加一个新行: pod 'SDWebImage', '~>3.6' 然后从 Xcode 工作区文件夹中的终端运行命令“pod update”。跨度>
  • 仍然下载“SDWebImage”并将其添加到我的项目中?
【解决方案2】:

你的代码少了一行,这是我添加的。

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;

【讨论】:

  • 不会解决这个问题,因为它也是一个时间问题。
【解决方案3】:

原因是UITableView 正在重用创建的单元格。 因此,首先它会创建您的 CustomCell 类中的 10 个。

单元格 0
单元 1
...
细胞 9

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

块 0 -> 单元 0
块 1 -> 单元 1
...
块 9 -> 单元 9

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

但只要您开始滚动,UITableView 就会开始重复使用创建的单元格并开始新的下载。

所以首先它可能会重用 Cell 0 并创建块 10 -> Cell 0。

如果在选择单元 0 以供重复使用时块 0 尚未完成,您现在将有两个块想要将它们的图像放到同一个单元上。 导致以下两种情况:

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

这就是导致“闪烁”的原因。

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

解决方案

您需要能够取消排队的块,以便您的单元格被重复使用。

我会使用例如SDWebImageFastImageCache

【讨论】:

  • 哦,好的,我有办法防止这种情况发生吗?当我滚动回顶部等时,它似乎正在重新加载所有图像?
  • 点击按钮太快,更新了一个建议。
【解决方案4】:

你说的。您在另一个线程中捕获图像,以防止冻结。并且在 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;

【讨论】:

    【解决方案5】:

    以上答案是正确的。但无需在单元格中下载图像。您可以在适合您要求的 viewWillAppear 或 ViewDidLoad 中进行表格加载之前下载图像。

    【讨论】:

      【解决方案6】:

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

      您可以使用 (https://github.com/rs/SDWebImage) 异步下载图像。它非常简单快捷。

      我更喜欢这个库,因为它会处理你的缓存。写在下面的代码

      #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];
              });
          });
      

      也许这会对你有所帮助。

      【讨论】:

      • 我以前试过这个,但我没有明确需要将 SDWebImage 中的哪些文件添加到我的项目中?我是否只将名为“SDWebImage”的文件夹添加到我的项目中?
      • @burrGGG 是在您的项目中添加文件夹名称SDWebImage。添加“ImageIO.framework”并将其他链接器标志设置为-ObjC#import "UIImageView+WebCache.h"。你就完成了。如果您知道如何安装,也可以安装 pod 文件。
      • @burrGGG 使用 pod 'SDWebImage', '~&gt;3.6' 安装 pod。
      • 感谢您的帮助,我不太确定链接器标志是什么
      • @burrGGG 点击项目 > 构建设置(第 4 个选项) > 其他链接器标志 > 双击 > 点击下面的 + > 写入 -ObjC > 按回车键。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-27
      • 1970-01-01
      相关资源
      最近更新 更多