4

重複の可能性:
画像付きのテーブル ビュー、読み込みとスクロールが遅い

UITableViewサーバーから の画像をダウンロードするがUITableViewCells あります。tableView のスクロールが非常に遅いことがわかりました。

これはダウンロードの問題かもしれないと思っていましたが、ダウンロードが完了した後も表のスクロールが遅く、画像アイコンのサイズが小さいことに気付きました。

Google で検索しましたが、ヘルプが見つかりませんでした。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    btnBack.hidden = FALSE;

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.backgroundColor = [UIColor clearColor];

        cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
        cell.textLabel.textColor = [UIColor blackColor];
        cell.textLabel.highlightedTextColor = [UIColor blackColor];
    }

        cell.textLabel.text = [NSString stringWithFormat:@"     %@", [test.arrTitle objectAtIndex:indexPath.row]];

        NSString *Path;
        Path = [NSString stringWithFormat:@"http://%@",[test.arrImages objectAtIndex:indexPath.row]];
        NSLog(@"image-->%@",[test.arrImages objectAtIndex:indexPath.row]);
        NSString *strImage = Path;
        NSURL *url4Image = [NSURL URLWithString:strImage];    
        NSData *data = [NSData dataWithContentsOfURL:url4Image];
        image =[[UIImage alloc] initWithData:data];
        cell.imageView.image =image;
        [image release];

        return cell;
}
4

6 に答える 6

18

以下の私の最初の回答では、非同期画像検索に関連するいくつかの重要な問題を解決しようとしましたが、それでも根本的に制限されています。適切な実装では、すばやくスクロールした場合に、表示されているセルが画面外にスクロールされたセルよりも優先されるようにすることもできます。また、以前のリクエストのキャンセルもサポートします (必要に応じてキャンセルします)。

この種の機能を以下のコードに追加することもできますが、以下で説明するNSOperationQueueとのNSCacheテクノロジを利用するだけでなく、上記の問題にも対処する、確立された実績のあるソリューションを採用することをお勧めします。最も簡単な解決策は、UIImageView非同期の画像検索をサポートする確立されたカテゴリの 1 つを採用することです。AFNetworkingライブラリとSDWebImageライブラリの両方UIImageViewに、これらすべての問題を適切に処理するカテゴリがあります。


NSOperationQueueまたはGCDを使用して遅延読み込みを実行できます (さまざまな非同期操作テクノロジについては、同時実行プログラミング ガイドを参照してください)。前者には、許可される同時操作の数を正確に指定できるという利点があります。これは、Web から画像をロードする際に非常に重要です。これは、多くの Web サーバーが特定のクライアントから受け入れる同時要求の数を制限しているためです。

基本的な考え方は次のとおりです。

  1. 画像データのリクエストを別のバックグラウンド キューに送信します。
  2. 画像のダウンロードが完了したら、バックグラウンドで UI の更新を行うべきではないため、UI の更新をメイン キューにディスパッチします。
  3. ディスパッチされた最終的な UI 更新コードをメイン キューで実行するときは、UITableViewCellがまだ表示されていること、問題のセルが画面外にスクロールしたためにキューから取り出されて再利用されていないことを確認してください。そうしないと、間違った画像が一時的に表示される場合があります。

コードを次のコードのようなものに置き換えます。

まず、NSOperationQueue画像のダウンロードNSCacheと保存に使用する のプロパティを定義します。

@property (nonatomic, strong) NSOperationQueue *imageDownloadingQueue;
@property (nonatomic, strong) NSCache *imageCache;

次に、このキューとキャッシュを初期化しますviewDidLoad

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.imageDownloadingQueue = [[NSOperationQueue alloc] init];
    self.imageDownloadingQueue.maxConcurrentOperationCount = 4; // many servers limit how many concurrent requests they'll accept from a device, so make sure to set this accordingly

    self.imageCache = [[NSCache alloc] init];

    // the rest of your viewDidLoad
}

第三に、次のcellForRowAtIndexPathようになります。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    btnBack.hidden = FALSE;

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.backgroundColor = [UIColor clearColor];

        cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
        cell.textLabel.textColor = [UIColor blackColor];
        cell.textLabel.highlightedTextColor = [UIColor blackColor];
    }

    cell.textLabel.text = [NSString stringWithFormat:@"     %@", [test.arrTitle objectAtIndex:indexPath.row]];

    // code change starts here ... initialize image and then do image loading in background

    NSString *imageUrlString = [NSString stringWithFormat:@"http://%@", [test.arrImages objectAtIndex:indexPath.row]];
    UIImage *cachedImage = [self.imageCache objectForKey:imageUrlString];
    if (cachedImage) {
        cell.imageView.image = cachedImage;
    } else {
        // you'll want to initialize the image with some blank image as a placeholder

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

        // now download in the image in the background

        [self.imageDownloadingQueue addOperationWithBlock:^{

            NSURL *imageUrl   = [NSURL URLWithString:imageUrlString];    
            NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
            UIImage *image    = nil;
            if (imageData) 
                image = [UIImage imageWithData:imageData];

            if (image) {
                // add the image to your cache

                [self.imageCache setObject:image forKey:imageUrlString];

                // finally, update the user interface in the main queue

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // Make sure the cell is still visible

                    // Note, by using the same `indexPath`, this makes a fundamental
                    // assumption that you did not insert any rows in the intervening
                    // time. If this is not a valid assumption, make sure you go back
                    // to your model to identify the correct `indexPath`/`updateCell`

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

    return cell;
}

最後に、メモリ不足の状況でキャッシュを消去するコードを書きたくなるかもしれませんが、これは自動的に行われるため、ここでは追加の処理は必要ありません。シミュレーターでメモリ不足の状況を手動でシミュレートする場合、 がNSCache応答しないため、オブジェクトを追い出すことはありませんUIApplicationDidReceiveMemoryWarningNotificationが、実際の操作中にメモリが少なくなると、キャッシュが消去されます。実際には、NSCacheメモリ不足の状況自体に適切に応答しなくなったため、この通知のオブザーバーを追加し、メモリ不足の状況ではキャッシュを空にする必要があります。

他の最適化を提案するかもしれません (たとえば、イメージを永続的なストレージにキャッシュして、将来の操作を合理化することもできます。実際には、このすべてのロジックを自分のAsyncImageクラスに入れています) が、まず、これで基本的なパフォーマンスの問題が解決するかどうかを確認してください。

于 2012-10-04T07:01:41.163 に答える
1

これをあなたのUITableView cellForRowAtIndex:メソッドに書いてください

asyncImageView = [[AsyncImageView alloc]initWithFrame:CGRectMake(30,32,100, 100)];         
[asyncImageView loadImageFromURL:[NSURL URLWithString:your url]];
[cell addSubview:asyncImageView];
[asyncImageView release];

AsyncImageView classそのクラスのオブジェクトをインポートして作成する必要があります

于 2012-10-03T07:05:15.453 に答える
0

viewDidLoad画像を配列に保存し、それらを に入力してから、cellForRowAtIndexPath:ちょうどセットに入力することをお勧めします

cell.imageView.image = [yourImageArray objectAtIndex:indexPath.row];

遅さに関する限り、それはcellForRowAtIndexPathメソッドで URLDATA をダウンロードしてメイン スレッドをブロックするためです。そのため、スクロール中に画像がフェッチされない限り、アプリケーションが実行されるメイン スレッドがブロックされます。

于 2012-10-03T07:02:06.423 に答える
0

メインスレッド、つまり同期的に画像をロードしているため、スクロールが非常に遅くなります。バックグラウンド スレッドで同じことを非同期的に行うことができます。SDWebImage をご覧ください

于 2012-10-03T06:56:54.430 に答える
0

前に述べたように: cellForRowAtIndexPath で重い作業を行わないでください。GCD を使用することで簡単に回避できます。 ブロックを使用してバックグラウンド スレッドから画像を読み込む

于 2012-10-03T07:09:35.723 に答える
0

NSOperationQueue画像の遅延読み込みとカスタム tableviewcell を処理するには、 を使用する必要があります。ここでサンプルの例を取得できますNSOperationQueue

Google for tweetie custom tableviewcell 正しい方向に設定する必要があります。

Apple には、tableViews で画像をダウンロードするためのサンプル プロジェクトがあります: LazyTableImages

于 2012-10-03T07:12:41.643 に答える