50

まもなく、 にNSDictionary表示する必要がある画像の URL がありますUITableView。各セルにはタイトルと画像があります。セルが画面に入るたびに画像をダウンロードしたように見えたため、スクロールが遅れていましたが、これをうまく実現できました。ちょっと探したら出てきたSDWebImagegithubで。これにより、スクロールラグがなくなりました。それが何をしたのかは完全にはわかりませんが、何らかのキャッシングを行っていると信じていました。しかし!初めてアプリを開くたびに、画像が表示されず、下にスクロールして戻る必要があります。また、ホームボタンでアプリを終了して再度開くと、画面上の画像が表示されているため、キャッシュが機能しているように見えますが、1 つのセルを下にスクロールすると、次のセルには画像がありません。スクロールして元に戻るまで、またはクリックするまで。これはキャッシングがどのように機能するはずですか?または、Web からダウンロードした画像をキャッシュする最良の方法は何ですか? 画像はめったに更新されないので、プロジェクトにインポートするだけに近かったのですが、更新をアップロードせずに画像を更新できるようにしたいと思っています..

起動時にテーブルビュー全体のすべての画像をキャッシュからロードすることは不可能ですか(キャッシュに何かがある場合)? 画像のない細胞が時々見えるのはそのためですか?

はい、キャッシュとは何かを理解するのに苦労しています。

- 編集 -

同じサイズ (500x150) の画像のみでこれを試したところ、縦横エラーはなくなりましたが、上下にスクロールするとすべてのセルに画像がありますが、最初は間違っています。セルが数ミリ秒間表示された後、右の画像が表示されます。これは驚くほど面倒ですが、どうすればいいのでしょうか?.. 最初はキャッシュから間違ったインデックスを選択しているようです。ゆっくりスクロールすると、画像が間違った画像から正しい画像に点滅するのがわかります。速くスクロールすると、常に間違った画像が表示されると思いますが、スクロールが速いためわかりません。高速スクロールが遅くなり、最終的に停止すると、依然として間違った画像が表示されますが、スクロールが停止した直後に正しい画像に更新されます。私にも風習がありますUITableViewCellクラスですが、大きな変更は行っていません..まだコードをあまり調べていませんが、何が間違っているのかわかりません..多分私は間違った順序で何かを持っています.. java、c#、php などで多くのプログラムを作成しましたが、Objective-c を理解するのに苦労してい.hます.m

@interface FirstViewController : UITableViewController{

/**/
NSCache *_imageCache;
}

(他の変数の中で) in FirstViewController.h. これは正しくありませんか?

ここに私のcellForRowAtIndexPath.

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

if (cell == nil)
{
    cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

NSMutableArray *marr = [hallo objectAtIndex:indexPath.section];
NSDictionary *dict = [marr objectAtIndex:indexPath.row];

NSString* imageName = [dict objectForKey:@"Image"];
//NSLog(@"url: %@", imageURL);

UIImage *image = [_imageCache objectForKey:imageName];

if(image)
{
    cell.imageView.image = image;
}
else
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSString* imageURLString = [NSString stringWithFormat:@"example.com/%@", imageName];
        NSURL *imageURL = [NSURL URLWithString:imageURLString];
        UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];

        if(image)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                CustomCell *cell =(CustomCell*)[self.tableView cellForRowAtIndexPath:indexPath];
                if(cell)
                {
                    cell.imageView.image = image;
                }
            });
            [_imageCache setObject:image forKey:imageName];
        }
    });
}

cell.textLabel.text = [dict objectForKey:@"Name"];

return cell;
}
4

7 に答える 7

78

キャッシュとは、必要なデータのコピーを保持することを意味するため、低速なソースからデータをロードする必要はありません。たとえば、マイクロプロセッサには多くの場合、データのコピーを保持するキャッシュ メモリがあり、RAM にアクセスする必要がありません。多くの場合、ハード ディスクにはメモリ キャッシュがあり、ファイル システムは最近アクセスされたデータ ブロックにすばやくアクセスできます。

同様に、アプリがネットワークから大量の画像を読み込む場合、必要になるたびに画像をダウンロードするのではなく、デバイスにキャッシュする方がよい場合があります。それを行う方法はたくさんあります - すでに見つけたようです。ダウンロードした画像をアプリの /Library/Caches ディレクトリに保存することをお勧めします。特に、画像が変更されることが予想されない場合はそうです。セカンダリ ストレージからイメージをロードすると、ネットワーク経由でイメージをロードするよりもはるかに高速になります。

必要なイメージをメモリに保持するための、あまり知られていないNSCacheクラスにも興味があるかもしれません。NSCache は辞書のように機能しますが、メモリが不足すると内容の一部を解放し始めます。最初に特定の画像のキャッシュを確認できます。見つからない場合はキャッシュ ディレクトリを調べ、見つからない場合はダウンロードできます。これらのいずれも、アプリを初めて実行するときの画像の読み込みを高速化するものではありませんが、アプリが必要なもののほとんどをダウンロードすると、応答性が大幅に向上します。

于 2012-07-16T20:10:05.367 に答える
25

Caleb はキャッシングの質問にうまく答えたと思います。NSCache画像を取得するときに UI を更新するプロセスについて触れようとしました_imageCache

まず、テーブルビューのオペレーション キュー プロパティを定義します。

@property (nonatomic, strong) NSOperationQueue *queue;

次に、viewDidLoadこれを初期化します。

self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;

ではcellForRowAtIndexPath、次のことができます。

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

    // set the various cell properties

    // now update the cell image

    NSString *imagename = [self imageFilename:indexPath]; // the name of the image being retrieved

    UIImage *image = [_imageCache objectForKey:imagename];

    if (image)
    {
        // if we have an cachedImage sitting in memory already, then use it

        cell.imageView.image = image;
    }
    else
    {
        cell.imageView.image = [UIImage imageNamed:@"blank_cell_image.png"];

        // the get the image in the background

        [self.queue addOperationWithBlock:^{

            // get the UIImage

            UIImage *image = [self getImage:imagename];

            // if we found it, then update UI

            if (image)
            {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // if the cell is visible, then set the image

                    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
                    if (cell)
                        cell.imageView.image = image;
                }];

                [_imageCache setObject:image forKey:imagename];
            }
        }];
    }

    return cell;
}

I only mention this as I've seen a few code samples floating around on SO recently that use GCD to update the appropriate UIImageView image property, but in the process of dispatching the UI update back to the main queue, they employ curious techniques (e.g., reloading the cell or table, just updating the image property of the existing cell object returned at the top of the tableView:cellForRowAtIndexPath (which is a problem if the row has scrolled off the screen and the cell has been dequeued and is being reused for a new row), etc.). By using cellForRowAtIndexPath (not to be confused with tableView:cellForRowAtIndexPath), you can determine if the cell is still visible and/or if it may have scrolled off and been dequeued and reused.

于 2012-07-16T20:25:09.380 に答える
13

最も簡単な解決策は、ストレス テスト済みの頻繁に使用されるものを使用することです。

SDWebImage は、同様の問題を解決するのに役立つ強力なツールであり、ココア ポッドで簡単にインストールできます。ポッドファイル内:

platform :ios, '6.1'
pod 'SDWebImage', '~>3.6'

セットアップ キャッシュ:

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image)
{
    // image is not nil if image was found
}];

キャッシュ イメージ:

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

https://github.com/rs/SDWebImage

于 2014-10-24T16:53:08.447 に答える
1

間違った画像に関する質問の一部については、セルの再利用が原因です。セルの再利用とは、既存のセルが見えなくなることを意味します (たとえば、画面を下にスクロールしたときに画面の上部から外れるセルは、下から再び戻ってくるセルです)。間違った画像。しかし、セルが表示されると、適切な画像を取得するためのコードが実行され、適切な画像が取得されます。

セルの「prepareForReuse」メソッドでプレースホルダーを使用できます。この関数は、主に、セルを再利用するために値をリセットする必要がある場合に使用されます。ここにプレースホルダーを設定すると、間違った画像が表示されなくなります。

于 2016-06-01T13:46:39.347 に答える
1

DLImageLoaderのようなユーザーにとっては良いと思います。詳細 -> https://github.com/AndreyLunevich/DLImageLoader-iOS

[[DLImageLoader sharedInstance] loadImageFromUrl:@"image_url_here"
                                   completed:^(NSError *error, UIImage *image) {
                                        if (error == nil) {
                                            imageView.image = image;
                                        } else {
                                            // if we got an error when load an image
                                        }
                                   }];
于 2014-08-14T11:22:26.427 に答える