10

がありUICollectionViewますが、同じ方法を に適用する必要がありますUITableViews。各セルには、ディスクからロードした画像が含まれていますが、これは低速な操作です。これを軽減するために、非同期ディスパッチ キューを使用します。これは問題なく動作しますが、すばやくスクロールすると、これらの操作が積み重なり、セルの画像が順番に変化し、最終的に最後の呼び出しで停止します。

過去に、セルがまだ表示されているかどうかを確認し、表示されていない場合は続行しません。ただし、これは UICollectionView では機能せず、とにかく効率的ではありません。NSOperationsセルを変更するための最後の呼び出しのみが通過するように、キャンセルできるを使用するように移行することを検討しています。メソッドで操作が終了したかどうかを確認し、終了していprepareForReuseない場合はキャンセルすることで、これを行うことができます。誰かが過去にこの問題に対処し、いくつかのヒントや解決策を提供できることを願っています。

4

3 に答える 3

21

セッション211-iOSでの同時ユーザーインターフェイスの構築(WWDC 2012から)では、バックグラウンドタスクが追いつくにつれてセルの画像が変化する問題について説明します(38分15秒から開始)。

この問題を解決する方法は次のとおりです。バックグラウンドキューに画像を読み込んだ後、インデックスパスを使用して、その画像を含むアイテムを表示している現在のセル(存在する場合)を見つけます。セルを取得したら、セルの画像を設定します。nilを取得した場合、そのアイテムを現在表示しているセルはないため、画像を破棄するだけです。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.imageView.image = nil;
    dispatch_async(myQueue, ^{
        [self bg_loadImageForRowAtIndexPath:indexPath];
    });
    return cell;
}

- (void)bg_loadImageForRowAtIndexPath:(NSIndexPath *)indexPath {
    // I assume you have a method that returns the path of the image file.  That
    // method must be safe to run on the background queue.
    NSString *path = [self bg_pathOfImageForItemAtIndexPath:indexPath];
    UIImage *image = [UIImage imageWithContentsOfFile:path];
    // Make sure the image actually loads its data now.
    [image CGImage];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self setImage:image forItemAtIndexPath:indexPath];
    });
}

- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
    // If cell is nil, the following line has no effect.
    cell.imageView.image = image;
}

これは、ビューが不要になった画像を読み込むバックグラウンドタスクの「積み重ね」を減らす簡単な方法です。NSMutableSetインスタンス変数を自分に与えます:

@implementation MyViewController {
    dispatch_queue_t myQueue;
    NSMutableSet *indexPathsNeedingImages;
}

画像を読み込む必要がある場合は、セルのインデックスパスをセットに入れ、セットから1つのインデックスパスを取り出してその画像を読み込むタスクをディスパッチします。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.imageView.image  = nil;
    [self addIndexPathToImageLoadingQueue:indexPath];
    return cell;
}

- (void)addIndexPathToImageLoadingQueue:(NSIndexPath *)indexPath {
    if (!indexPathsNeedingImages) {
        indexPathsNeedingImages = [NSMutableSet set];
    }
    [indexPathsNeedingImages addObject:indexPath];
    dispatch_async(myQueue, ^{ [self bg_loadOneImage]; });
}

セルが表示されなくなった場合は、セルのインデックスパスをセットから削除します。

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
    [indexPathsNeedingImages removeObject:indexPath];
}

画像を読み込むには、セットからインデックスパスを取得します。セットの競合状態を回避するために、このためにメインキューにディスパッチする必要があります。

- (void)bg_loadOneImage {
    __block NSIndexPath *indexPath;
    dispatch_sync(dispatch_get_main_queue(), ^{
        indexPath = [indexPathsNeedingImages anyObject];
        if (indexPath) {
            [indexPathsNeedingImages removeObject:indexPath];
        }
    }

    if (indexPath) {
        [self bg_loadImageForRowAtIndexPath:indexPath];
    }
}

方法は上記bg_loadImageForRowAtIndexPath:と同じです。ただし、実際にセルに画像を設定するときは、セットからセルのインデックスパスも削除する必要があります。

- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
    // If cell is nil, the following line has no effect.
    cell.imageView.image = image;

    [indexPathsNeedingImages removeObject:indexPath];
}
于 2012-12-17T21:41:41.257 に答える
8

Web から画像をダウンロードして非同期にキャッシュするSDWebImageオープン ソース フレームワークを使用してみましたか? それは絶対にうまく機能しUITableViews、と同じくらい良いはずUICollectionViewsです。setImage:withURL:拡張機能のメソッドを使用するだけで、UIImageView魔法のように機能します。

于 2012-12-17T20:58:39.020 に答える
-1

約 400 要素の UITableView で同じ問題が発生し、残忍だが機能する解決策を見つけました。セルを再利用しないでください! 画像の量と大きさにもよりますが、実際にはiOSは未使用のセルの割り当てを解除するのに非常に効率的です... 繰り返しますが、これは純粋主義者が提案するものではありませんが、非同期ロードと組み合わせると、すばやくエラーが発生せず、そうする必要はありません複雑な同期ロジックを処理します。

于 2012-12-17T20:58:59.890 に答える