0

私はこれで頭を壁にぶつけて、役に立たない解決策を遠くまで探しました:

Web から取得した大量のデータがあり、Loren Brichter のABTableViewCellを使用して、各セルの contentView 内のすべてを描画して UILabels と UIImageViews のスクロールが遅くなるのを防ぐことで、スムーズに実行しています。

これはテキストの表示には最適ですが、画像のダウンロードに時間がかかるため問題が発生します。対応する画像がダウンロードされると、表示される各セルの contentView を強制的に再描画する方法が見つからないようです。ラベルと imageView を描画しているのではなく、メモリを節約するために contentView だけを描画していることを指摘しておく必要があります。

現在、テーブルは次のように動作します。

  • 読み込み: テキスト表示、画像なし
  • 上または下にスクロール: セルが画面外に移動すると、最終的に画像が表示されます

サンプルプロジェクトはこちら

コード:

ABTableViewCell.h

@interface ABTableViewCell : UITableViewCell
{
    UIView *contentView;
}

ABTableViewCell.m

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

}

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

TableCellLayout.h

#import "ABTableViewCell.h"

@interface TableCellLayout : ABTableViewCell {

}

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

TableCellLayout.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];
}

TableViewController.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) に何かを入れる必要があると思いますが、本来の方法で機能させることができないようです。考え?どうもありがとう!

最終編集: 解決策

予想どおり、問題は、呼び出しが完了したときに、表示されているセルが再描画され、ダウンロードされた画像に更新されるように指示されていなかったことです。

以下の回答で提供されるヘルプを使用して、自分のニーズに適したソリューションをまとめました。

非同期イメージ ダウンローダーが呼び出す最終メソッドにコールバックを追加しました。

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;

他に 1 つまたは 2 つの微調整があった可能性がありますが、これらがここでの主な問題でした。SOコミュニティに再び感謝します!

4

5 に答える 5

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 に答える
0

uiimageview の代わりに AsyncImageView を使用する

于 2012-08-16T06:04:04.010 に答える
0

Apple TableView Lazy Loading を使用できます。画像を非同期でダウンロードするサンプル コードがあります。以下のリンクを参照してください

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 のイメージ プロパティの設定がメイン スレッドで行われることを確認します。画像ビューの画像を設定しても、表示されている場合にその四角形を無効にしてはならない理由はありません。

セルを再利用している場合は、ロードが正しくキャンセルされていることも確認してください。

于 2012-08-16T05:16:19.903 に答える
0

reloadDatatableView に... またはもう少し洗練されたリロードを指示できます。

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

追加する編集(OPのソースを見た後):

私はあなたのプロジェクトを調べましたが、問題はあなたのアーキテクチャにあります。AsyncImageView画像を非同期にロードするためにのみ使用しているため、誤用していますが、画像のロードと表示の両方を行うように設計されています。これが、データがいつ取得されたかを知らせる「コールバック」機能がない理由です。

CellLayoutの画像プロパティを代わりに UIImageView プロパティに置き換えたほうがよいでしょう。(UIImageView はとにかくより効率的に描画できることに注意してくださいimage drawAtPoint)。

そう:

  1. 代わりにプロパティCellLayoutを使用するようにクラスを変更しますUIImageViewUIImage
  2. cellForRowAtIndexPathAsyncImageView をセルのプロパティとして直接設定するように変更します。

プレースホルダーをサポートしたい場合は、それを 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;

CellLayout.h

@property (nonatomic, strong) UIImageView *cellImageView;

CellLayout.m

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