2013-10-30 13 views
9

私のセルイメージをロードするために非同期ブロック(グランド中央ディスパッチ)を使用しています。しかし、スクロールしても速く表示されますが、正しいものがロードされるまで非常に高速です。これは一般的な問題だと確信していますが、私はそれを回避することはできません。非常に少なくともテーブルビュー非同期ロードされたセルイメージはスクロールして高速にスクロールするとちらつきます

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


// Load the image with an GCD block executed in another thread 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]]; 

    dispatch_async(dispatch_get_main_queue(), ^{ 

     UIImage *offersImage = [UIImage imageWithData:data]; 

     cell.imageView.image = offersImage; 
    }); 
}); 

cell.textLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] title]; 
cell.detailTextLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] subtitle]; 

return cell; 
} 
+0

http://stackoverflow.com/questions/19326284/what-is-the-properway-to-use-nscache-with-dispatch-async-in-a-reusable-table-ce/19327472#を参照してください。 19327472またはhttp://stackoverflow.com/questions/16663618/async-image-loading-from-url-inside-a-uitableview-cell-image-changes-to-wrong/16663759#16663759 – Rob

+0

私の投稿http ://stackoverflow.com/a/26147814/305205​​9ありがとうございました! –

+0

私はこのブログの記事を読むことをお勧めしますhttp://www.natashatherobot.com/how-to-download-images-asynchronously-and-make-your-uitableview-scroll-fast-in-ios/ – pprochazka72

答えて

17

、おそらくあなたのdispatch_async前に(場合には、それが再使用される細胞である)のセルから画像を削除する:

cell.imageView.image = [UIImage imageNamed:@"placeholder.png"]; 

それとも

cell.imageView.image = nil; 

UITableViewメソッドを使用して、cellForRowAtIndexPath:nilを返すように、問題のセルが画面上にまだ表示されていることを確認しますその行のL UITableViewDataDelegate方法tableView:cellForRowAtIndexPath:と混同しないように、もはや可視である)、例えば:

率直
static NSString *CellIdentifier = @"Cell"; 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 

cell.imageView.image = [UIImage imageNamed:@"placeholder.png"]; 

// Load the image with an GCD block executed in another thread 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]]; 

    if (data) { 
     UIImage *offersImage = [UIImage imageWithData:data]; 
     if (offersImage) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; 

       if (updateCell) { 
        updateCell.imageView.image = offersImage; 
       } 
      }); 
     } 
    } 
}); 

cell.textLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] title]; 
cell.detailTextLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] subtitle]; 

return cell; 

、あなたはまた、例えば、(不必要に再取得画像を避けるためにキャッシュを使用しなければなりません下にスクロールしてスクロールアップすると、それらの以前のセルのイメージに対してネットワーク要求を発行したくない)。さらに、UIImageViewカテゴリ(SDWebImageまたはAFNetworkingに含まれているカテゴリなど)のいずれかを使用する必要があります。これは、非同期の画像読み込みを実現しますが、キャッシュ処理(数秒前に検索した画像を再検索しないでください)、まだ起こっていない画像の取り消し(例えば、ユーザーがすぐに行100にスクロールすると、 100行目の画像をユーザーに表示する前に最初の99が検索されるのを待っています)。

+0

潜在的可能性もあります非同期コールが完了する前にセルを再利用できる問題が発生し、正しいイメージが以前のコールのイメージに置き換えられます。テーブルビューのセルの非同期ロードイメージは簡単ではありません。 – kubi

+0

@kubi合意しましたが、私のコードサンプルは 'updateCell'をチェックしてその問題に対処し、セルが再利用されていないことを確認しました。しかし、あなたはそれが自明ではなく、直接の問題に対する私の戦術的な修正は、他のさまざまな問題(キャッシュ、バックログされた要求など)に対処しないので、これらのUIImageViewカテゴリのうちの1つを使用するように、それは非常に簡単になります。 – Rob

+0

@Rob、私はあなたが次の質問を見て嬉しいだろうhttp://stackoverflow.com/questions/33397726/download-images-from-a-server-to-display-on-the-collectionview – hotspring

0

問題は、メインスレッドでイメージをダウンロードすることです。そのため、バックグラウンドキューをディスパッチ:

dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL); 

    // execute a task on that queue asynchronously 
    dispatch_async(myQueue, ^{ 
     NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]]; 
//UI updates should remain on the main thread 
UIImage *offersImage = [UIImage imageWithData:data]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 

     UIImage *offersImage = [UIImage imageWithData:data]; 

     cell.imageView.image = offersImage; 
    }); 
    }); 
+0

user2920762はメインスレッドでイメージをダウンロードしていません。彼はグローバル(すなわち、バックグラウンド)キューを使用しています(これは優れていませんが、メインキューよりも優れています)。彼は、単にセルの再利用を正しく処理していないだけです(非同期検索が行われるまでにセルが画面からスクロールしたという考えを適切に処理していない)。 – Rob

+0

'UIImage * offersImage = [UIImage imageWithData:data];'これはコード内のメインスレッド上で実行されます。 –

+0

合意。 (あなたが実際に 'imageWithData'を2回、バックグラウンド・キューとメイン・キューに2回行っていますが、これはあなたが意図していないと確信しています。)しかし、主な問題はバックグラウンド・キューでの取得です新しいイメージがダウンロードされている間に古いイメージが表示されていて、イメージがダウンロードされている間にセルが別の行に再利用されている可能性があるという事実を処理していないほどです。ですから、バックグラウンドキューで 'imageWithData'を実行するべきですが、問題は解決しません。問題はディスパッチの前に 'cell.imageView.image'を初期化できないことです。 – Rob

1

これは、非同期画像の読み込みに問題がある... は、あなたが任意の時点で5つの表示される行があるとしましょう。 スクロール速度が速く、インスタンス10行分スクロールすると、tableView:cellForRowAtIndexPathが10回呼び出されます。問題は、これらの呼び出しが画像が返されるよりも速く、異なるURLから10個の保留中の画像があることです。 イメージが最終的にサーバーから戻ってくると、非同期ローダーに入れたセルにイメージが返されます。 5つのセルしか再利用していないので、これらのイメージの一部は、サーバーからダウンロードされるため、各セルに2回表示されるため、ちらつきが表示されます。また、再利用のセルから前の画像が残っていると、新しいイメージが割り当てられているちらつき効果の原因となりますよう、方法をダウンロードする非同期を呼び出す前に

cell.imageView.image = nil

を呼び出すことを忘れないでください。

これは、各セルに表示する必要がある画像の最新URLを保存しておき、その画像がサーバーから戻ってきたときに、そのURLをリクエストで確認します。同じでない場合は、後でそのイメージをキャッシュします。 リクエストをキャッシュするには、NSURLRequestNSURLConnectionクラスをチェックしてください。

どのサーバー通信でもAFNetworkingを使用することを強くお勧めします。

幸運を祈る!

2

ちらつきの原因は、スクロール中に複数の画像のダウンロードを開始することです。セルが画面に表示されるたびに新しい要求が実行され、古い要求が完了していない可能性があります。要求が完了すると画像はセルにセットされますので、スクロールすると速くなります。セルを3回= 3回要求すると、そのセルに3画像=フリッカーが設定されます。

私は同じ問題を抱えていました。ここに私のアプローチがあります。 必要なすべてのビューを持つカスタムセルを作成します。各セルには独自のダウンロード操作があります。セルの-prepareForReuseメソッドで。私はイメージをゼロにしてリクエストをキャンセルします。

このように、各セルでは、1つの要求操作= 1つの画像=フリッカーなししかありません。

AFNetworkingを使用しても、画像のダウンロードをキャンセルしない場合は同じ問題が発生する可能性があります。

+0

私はimageview、image = nilのみを設定し、それは仕事をしました! – Elsammak