0

我一直在用头撞墙,并四处寻找解决方案,但无济于事:

我有大量从网络上提取的数据,我正在使用 Loren Brichter 的ABTableViewCell通过在每个单元格的 contentView 内绘制所有内容来使其平稳运行,以避免 UILabels 和 UIImageViews 减慢滚动速度。

这非常适合显示文本,但由于下载图像需要时间,我遇到了图像问题。一旦下载了相应的图像,我似乎无法找到一种方法来强制显示的每个单元格的 contentView 重新绘制自身。我必须指出我不是在绘制标签和图像视图,而只是为了节省内存而绘制 contentView。

现在该表的行为如下:

  • 加载:显示文本,无图像
  • 向上或向下滚动:一旦单元格移出屏幕,图像最终会显示出来

示例项目在这里

代码:

ABTableViewCell.h

@interface ABTableViewCell : UITableViewCell
{
    UIView *contentView;
}

ABTableViewCell.m

- (void)setNeedsDisplay
{
    [contentView setNeedsDisplay];
    [super setNeedsDisplay];

}

- (void)drawContentView:(CGRect)r
{
    // subclasses implement this
}

表格单元布局.h

#import "ABTableViewCell.h"

@interface TableCellLayout : ABTableViewCell {

}

@property (nonatomic, copy) UIImage *cellImage;
@property (nonatomic, copy) NSString *cellName;

表格单元布局.m

#import "TableCellLayout.h"

@implementation TableCellLayout

@synthesize cellImage, cellName;

- (void)setCellName:(NSString *)s
{
    cellName = [s copy];
    [self setNeedsDisplay];
}

- (void)setCellImage:(UIImage *)s
{
    cellImage = [s copy];
    [self setNeedsDisplay];
}

- (void)drawContentView:(CGRect)r
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextFillRect(context, r);

    [cellImage drawAtPoint:p];
    [cellName drawAtPoint:p withFont:cellFont];
}

表视图控制器.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    TableCellLayout *cell = (TableCellLayout *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(cell == nil)
    {
        cell = [[TableCellLayout alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    cell.cellName = [[array valueForKey:@"name"] objectAtIndex:indexPath.row];
    cell.cellImage = [UIImage imageNamed:@"placeholder.png"]; // add a placeholder    

    NSString *imageURL = [[array valueForKey:@"imageLink"] objectAtIndex:indexPath.row];
    NSURL *theURL = [NSURL URLWithString:imageURL];

    if (asynchronousImageLoader == nil){
        asynchronousImageLoader = [[AsynchronousImages alloc] init];
    }
       [asynchronousImageLoader loadImageFromURL:theURL];

        cell.cellImage = asynchronousImageLoader.image;

return cell;
}

这是图像准备好后 AsynchronousImageLoader 调用的最后一个方法:

- (void)setupImage:(UIImage*)thumbnail {
    self.image = thumbnail;
        [self setNeedsLayout];

}

一旦下载了行的图像,我只需要正确的方法来告诉我的可见单元格重新绘制自己。我想我应该在最终方法(setupImage)中添加一些东西——但我似乎无法让它按应有的方式工作。想法?非常感谢!

最终编辑:解决方案

是的,正如所怀疑的那样,问题在于调用完成后,没有告诉可见单元重新绘制并更新到下载的图像。

我使用以下答案提供的帮助来组合一个适合我需求的解决方案:

在异步图片下载器调用的 final 方法中添加了一个回调:

AsyncImageView.m

 - (void)setupImage:(UIImage*)thumbnail {

      self.cellImage = thumbnail;
[cell setNeedsDisplay];

}

注意:我还在图像下载器类的初始化中设置了一个本地占位符图像,只是为了美化一点。

然后在我的 cellForRowAtIndexPath 中:

NSURL *theURL = [NSURL URLWithString:imageURL];

AsyncImageView *aSync = [[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 20, cell.bounds.size.height)];

[aSync loadImageFromURL:theURL];
cell.cellImageView = aSync;

return cell;

可能还有一两个其他调整,但这些是这里的主要问题。再次感谢 SO 社区!

4

5 回答 5

0

您可以使用 Apple TableView 延迟加载。他们有异步下载图像的示例代码。请参阅下面的链接

Apple LazyTableImages

在 AsynchronousImages 类中,您可以添加一个属性 NSIndexPath 并且 AsynchronousImages 上的委托应该更改。请参阅下面的代码

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    TableCellLayout *cell = (TableCellLayout *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(cell == nil)
    {
        cell = [[TableCellLayout alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    cell.cellName = [[array valueForKey:@"name"] objectAtIndex:indexPath.row];
    cell.cellImage = [UIImage imageNamed:@"placeholder.png"]; // add a placeholder    

    NSString *imageURL = [[array valueForKey:@"imageLink"] objectAtIndex:indexPath.row];
    NSURL *theURL = [NSURL URLWithString:imageURL];


    AsynchronousImages *asynchronousImageLoader = [[AsynchronousImages alloc] init];
    asynchronousImageLoader.indexPath = indexPath;
    [asynchronousImageLoader loadImageFromURL:theURL];
    return cell;
}

//Delegate should be
- (void)setupImage:(UIImage*)thumbnail index:(NSIndexPath*) indePath {
    TableCellLayout *cell = (TableCellLayout *) [tableView cellForRowAtIndexPath:indexPath];
    if(cell) {
      cell.cellImage = thumbnail;
      [cell setNeedsDisplay];
    }
}
于 2012-08-16T04:02:40.570 回答
0

验证 UIImage 的创建和 UIImageView 的图像属性的设置发生在主线程上。没有理由设置图像视图的图像不应该使其 rect 可见。

如果您正在重用单元格,还请确认您的负载已正确取消。

于 2012-08-16T05:16:19.903 回答
0

使用AsyncImageView 代替 uiimageview

于 2012-08-16T06:04:04.010 回答
0

您可以告诉您的 tableView reloadData... 或稍微更精致的重新加载:

  [self.tableView reloadRowsAtIndexPaths:self.tableView.indexPathsForVisibleRows withRowAnimation:UITableViewRowAnimationNone];

编辑添加(查看 OP 的源代码后):

我查看了您的项目,问题出在您的架构上。您在滥用AsyncImageView,因为您仅使用它来异步加载图像 - 而它旨在同时加载和显示图像。这就是为什么它没有“回调”功能来让您知道何时检索到数据。

你最好用CellLayoutUIImageView 属性替换你的 image 属性。(请注意,UIImageView 在绘图方面比其他方式更有效image drawAtPoint)。

所以:

  1. 更改您的CellLayout课程以使用UIImageView属性而不是UIImage
  2. 更改您cellForRowAtIndexPath以直接在 Cell 上将 AsyncImageView 设置为属性。

如果您想支持占位符,则应将其添加到您的 AsyncImageView 类中 - 以便它知道在下载内容时要显示什么。

cellForRowAtIndexPath:

NSURL *theURL = [NSURL URLWithString:imageURL];

AsyncImageView *aSync = [[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 20, cell.bounds.size.height)];

[aSync loadImageFromURL:theURL];
cell.cellImageView = aSync;

return cell;

单元布局.h

@property (nonatomic, strong) UIImageView *cellImageView;

单元格布局.m

- (void)setCellImageView:(UIImageView *)s
{
    [cellImageView removeFromSuperview];
    cellImageView = s;
    [self addSubview:cellImageView];
}
于 2012-08-16T07:28:30.627 回答
0

确保您正在更新主线程中的单元格图像。UI 更新仅在此处完成时才会出现,这就是为什么您仅在触摸和滚动时才能看到更新的原因。

if(cell) {

    dispatch_async(dispatch_get_main_queue(), ^{

        cell.cellImage = thumbnail;
        [cell setNeedsDisplay];

    });

}

[编辑]

您需要将单元格作为图像加载器的代表并拥有异步重绘机制。

......
    AsynchronousImages *asynchronousImageLoader = [[AsynchronousImages alloc] init];
    asynchronousImageLoader.delegate = cell;
    [asynchronousImageLoader loadImageFromURL:theURL];

    return cell;
}

并将委托回调代码放在单元实现中。

- (void)setupImage:(UIImage*)thumbnail {

      self.cellImage = thumbnail;

}
于 2012-08-16T07:37:37.060 回答