UITableViewセル内のURLからの非同期画像の読み込み-スクロール中に画像が誤った画像に変わる


158

UITableViewセル内の画像を非同期で読み込む2つの方法を記述しました。どちらの場合も画像は正常に読み込まれますが、テーブルをスクロールすると、スクロールが終了して画像が正しい画像に戻るまで、画像が数回変化します。なぜこれが起こっているのか私にはわかりません。

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:
                                                       @"http://myurl.com/getMovies.php"]];
        [self performSelectorOnMainThread:@selector(fetchedData:)
                               withObject:data waitUntilDone:YES];
    });
}

-(void)fetchedData:(NSData *)data
{
    NSError* error;
    myJson = [NSJSONSerialization
              JSONObjectWithData:data
              options:kNilOptions
              error:&error];
    [_myTableView reloadData];
}    

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    // Return the number of rows in the section.
    // Usually the number of items in your array (the one that holds your list)
    NSLog(@"myJson count: %d",[myJson count]);
    return [myJson count];
}
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

        myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        if (cell == nil) {
            cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
        }

        dispatch_async(kBgQueue, ^{
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];

            dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
            });
        });
         return cell;
}

... ...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

            myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
            if (cell == nil) {
                cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
            }
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]];
    NSURLRequest* request = [NSURLRequest requestWithURL:url];


    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse * response,
                                               NSData * data,
                                               NSError * error) {
                               if (!error){
                                   cell.poster.image = [UIImage imageWithData:data];
                                   // do whatever you want with image
                               }

                           }];
     return cell;
}

5
実際のセルに情報を保存しようとしています。これは悪い、非常に悪い。情報をn配列(または類似の配列)に格納してから、セルに表示する必要があります。この場合の情報は、実際のUIImageです。はい、非同期で読み込みますが、配列に読み込みます。
Fogmeister

1
@Fogmeisterあなたは参照していposterますか?これはおそらく彼のカスタムセルのイメージビューであるため、EXEC_BAD_ACCESSの動作は完全に正しいものです。セルをモデルデータのリポジトリとして使用するべきではないことは正しいですが、彼がそうしているとは思いません。彼はそれ自体を提示するために必要なものをカスタムセルに与えているだけです。さらに、これはより微妙な問題ですが、テーブルビューをサポートするモデル配列に画像自体を格納することには注意が必要です。画像キャッシュメカニズムを使用する方が良いです。モデルオブジェクトはそのキャッシュから取得する必要があります。
Rob

1
はい、まさに私のポイントです。リクエスト(完全に表示されています)を見ると、画像を非同期でダウンロードして、セルのimageViewに直接入れています。(したがって、データ、つまり画像を格納するためにセルを使用します)。彼がすべきことは、オブジェクトを参照し、そのオブジェクト(配列またはどこかに含まれる)からのイメージを要求することです。オブジェクトにまだ画像がない場合は、プレースホルダーを返して画像をダウンロードする必要があります。次に、画像がダウンロードされて表示する準備ができたら、セルを更新できるようにテーブルに通知します(表示されている場合)。
フォグマイスター2013年

1
彼がしていることは、テーブル内のそのセルにスクロールするたびにダウンロードを強制します。画像を永続的に保存するかどうかは彼次第ですが、少なくともテーブルビューの存続期間は保存してください。
フォグマイスター2013年

1
正確に:Dこれにより、URLから画像を1回だけ取得する必要があります。Facebook Friend Pickerのようなものでこれを見るでしょう。開始すると、すべてのアバターは灰色のプレースホルダーです。次に、スクロールすると、動きに合わせてすべてが塗りつぶされます。ただし、以前に表示されたセルにスクロールして戻ると、すでにダウンロードされた画像がすぐに表示されます。
フォグマイスター2013年

回答:


230

あなたが迅速な戦術的な修正を探していると仮定すると、あなたがする必要があるのは、セル画像が初期化されていること、そしてセルの行がまだ見えることを確認することです。例えば:

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

    cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (data) {
            UIImage *image = [UIImage imageWithData:data];
            if (image) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                    if (updateCell)
                        updateCell.poster.image = image;
                });
            }
        }
    }];
    [task resume];

    return cell;
}

上記のコードは、セルが再利用されるという事実から生じるいくつかの問題に対処しています。

  1. バックグラウンドリクエストを開始する前にセルイメージを初期化していません(つまり、デキューされたセルの最後のイメージは、新しいイメージのダウンロード中も表示されます)。画像ビューのプロパティを確認しnilてください。そうしないと、image画像のちらつきが発生します。

  2. さらに微妙な問題は、本当に遅いネットワークでは、セルが画面から消える前に非同期リクエストが完了しない可能性があることです。UITableViewメソッドcellForRowAtIndexPath:(同様の名前のUITableViewDataSourceメソッドと混同しないでください)を使用して、tableView:cellForRowAtIndexPath:その行のセルがまだ表示されているかどうかを確認できます。このメソッドはnil、セルが表示されていない場合に戻ります。

    問題は、asyncメソッドが完了するまでにセルがスクロールされ、さらに悪いことに、セルがテーブルの別の行に再利用されていることです。行がまだ表示されているかどうかを確認することで、画面からスクロールされた後の行の画像で誤って画像を更新していないことを確認できます。

  3. 目前の質問とは多少関係がありませんでしたが、現代の規則とAPIを活用するために、これを更新することを余儀なくされました。

    • バックグラウンドキューにNSURLSessionディスパッチするのではなく使用し-[NSData contentsOfURL:]ます。

    • dequeueReusableCellWithIdentifier:forIndexPath:ではなく使用しますdequeueReusableCellWithIdentifier:(ただし、その識別子にはセルプロトタイプまたはレジスタクラスまたはNIBを使用してください)。そして

    • Cocoaの命名規則に準拠したクラス名を使用しました(つまり、大文字で始めます)。

これらの修正があっても、問題があります。

  1. 上記のコードはダウンロードした画像をキャッシュしていません。つまり、画像を画面外にスクロールして画面に戻ると、アプリは画像を再度取得しようとする可能性があります。おそらく、あなたはあなたのサーバーのレスポンスヘッダは、によって提供されるかなり透過キャッシングを可能にすることを幸運に十分だろうNSURLSessionNSURLCache、そうでない場合、あなたが不要なサーバ要求を作り、はるかに遅いUXを提供することがあります。

  2. 画面外にスクロールするセルのリクエストはキャンセルしません。したがって、100行目にすばやくスクロールすると、その行の画像が、表示されなくなった前の99行のリクエストの背後にバックログされる可能性があります。常に、最適なUXのために可視セルの要求に優先順位を付けることを確認したいとします。

これらの問題に対処する最も簡単な修正UIImageViewは、SDWebImageAFNetworkingで提供されるようなカテゴリを使用することです。必要に応じて、上記の問題に対処するための独自のコードを作成できますが、それは多くの作業であり、上記のUIImageViewカテゴリはすでにこれを行っています。


1
ありがとう。回答を編集する必要があると思います。それが宣言される前に呼び出されてupdateCell。updateCell.poster.image = nilcell.poster.image = nil;
セゲフ2013年

1
私のアプリはたくさんのjsonを使用しているのでAFNetworking、間違いなくそうです。私はそれについて知っていましたが、それを使用するのが面倒でした。私は、キャッシュが単純なコード行でどのように機能するかを賞賛しています。[imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
セゲフ2013年

2
上記のすべてとSDWebImage(実際にはここで停止し、AFNetworkingを試す必要すらありませんでした)を試しましたが、これは断然最良の選択でした。@Robに感謝します。
mondousage 2015年

1
画像の読み込みが完了して画像ビューを更新したら、アクティビティインジケーターを削除します。唯一のトリックは、画像の取得中にセルがビューの外にスクロールし、セルが別の行に再利用される場合に何が起こるかを予測する必要があることです。既存のアクティビティインジケーターの存在を検出し、削除/更新する必要があります。それは、セルに既存のインジケータがないと仮定するだけではありません。
Rob

1
元の質問は「なぜこれがcellForRowAtIndexPathすばやくスクロールしたときに画像のちらつきを引き起こすのか」であり、その原因と修正方法を説明しました。しかし、それでも不十分である理由を説明し、いくつかのより深い問題を説明し、これらのライブラリのいずれかを使用してこれをより適切に処理する方がよいと主張しました(可視セルの要求を優先し、冗長ネットワークを回避するためにキャッシュします)リクエストなど)。「テーブルビューで画像のちらつきを止めるにはどうすればよいですか」という質問に対して、他に何を期待していたのかわかりません。
Rob

15

/ *私はこのようにしてそれをテストしました* /

手順1 = viewDidLoadメソッドで、次のようなテーブルにカスタムセルクラス(テーブルのプロトタイプセルの場合)またはnib(カスタムセルのカスタムnibの場合)を登録します。

[self.yourTableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:@"CustomCell"];

または

[self.yourTableView registerNib:[UINib nibWithNibName:@"CustomTableViewCell" bundle:nil] forCellReuseIdentifier:@"CustomCell"];

ステップ2 = UITableViewの「dequeueReusableCellWithIdentifier:forIndexPath:」メソッドを次のように使用します(このため、クラスまたはnibを登録する必要があります):

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
            CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell" forIndexPath:indexPath];

            cell.imageViewCustom.image = nil; // [UIImage imageNamed:@"default.png"];
            cell.textLabelCustom.text = @"Hello";

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // retrive image on global queue
                UIImage * img = [UIImage imageWithData:[NSData dataWithContentsOfURL:     [NSURL URLWithString:kImgLink]]];

                dispatch_async(dispatch_get_main_queue(), ^{

                    CustomTableViewCell * cell = (CustomTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
                  // assign cell image on main thread
                    cell.imageViewCustom.image = img;
                });
            });

            return cell;
        }

1
最後のブロック内でcellForRowAtIndexPathを呼び出すと、全体が2回発生しますか?
マークブリッジ

@MarkBridges、いいえ。実際には、ここでtableViewのcellForRowAtIndexPathメソッドを呼び出しています。同じ名前のtableViewのデータソースメソッドと混同しないでください。必要な場合、[self tableView:tableView cellForRowAtIndexPath:indexPath];のように呼び出すことができます。これで混乱が解消されることを願っています。
Nitesh Borad

14

この問題を解決するフレームワークは複数あります。いくつか例を挙げると:

迅速:

Objective-C:


検討に値する他のフレームワークがある場合は、提案を追加してください。
キーン2015

3
実際にSDWebImageはこの問題は解決しません。イメージをダウンロードするタイミングを制御できますが、これを行う許可について尋ねることなくSDWebImageイメージをに割り当てUIImageViewます。基本的に、問題の問題はこのライブラリではまだ解決されていません。
パルトロミエSemańczyk

問題の問題は、作者がセルが再利用されたかどうかをチェックしていなかったことでした。これは、SDWebImageを含むこれらのフレームワークで対処される非常に基本的な問題です。
キーン

SDWebImageはiOS 8から非常に遅れています。これは私のお気に入りのフレームワークの1つでしたが、今は非常にうまく機能するPinRemoteImageを使い始めています。
ジョーンカルドナ

@BartłomiejSemańczykそうです、この問題はSDWebimageで解決されません
Jan

9

スウィフト3

NSCacheを使用して、イメージローダーの独自のライト実装を記述します。 細胞の画像がちらつくことはありません!

ImageCacheLoader.swift

typealias ImageCacheLoaderCompletionHandler = ((UIImage) -> ())

class ImageCacheLoader {
    
    var task: URLSessionDownloadTask!
    var session: URLSession!
    var cache: NSCache<NSString, UIImage>!
    
    init() {
        session = URLSession.shared
        task = URLSessionDownloadTask()
        self.cache = NSCache()
    }
    
    func obtainImageWithPath(imagePath: String, completionHandler: @escaping ImageCacheLoaderCompletionHandler) {
        if let image = self.cache.object(forKey: imagePath as NSString) {
            DispatchQueue.main.async {
                completionHandler(image)
            }
        } else {
            /* You need placeholder image in your assets, 
               if you want to display a placeholder to user */
            let placeholder = #imageLiteral(resourceName: "placeholder")
            DispatchQueue.main.async {
                completionHandler(placeholder)
            }
            let url: URL! = URL(string: imagePath)
            task = session.downloadTask(with: url, completionHandler: { (location, response, error) in
                if let data = try? Data(contentsOf: url) {
                    let img: UIImage! = UIImage(data: data)
                    self.cache.setObject(img, forKey: imagePath as NSString)
                    DispatchQueue.main.async {
                        completionHandler(img)
                    }
                }
            })
            task.resume()
        }
    }
}

使用例

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "Identifier")
    
    cell.title = "Cool title"

    imageLoader.obtainImageWithPath(imagePath: viewModel.image) { (image) in
        // Before assigning the image, check whether the current cell is visible
        if let updateCell = tableView.cellForRow(at: indexPath) {
            updateCell.imageView.image = image
        }
    }    
    return cell
}

3
ありがとうございます。しかし、コードには少し問題があります。もしデータを試してみるなら?Data(contentsOf:url){// urlを場所に置き換えてください。それは多くの人を助けるでしょう。
カールフン

2
コードをそのまま使用して、ファイルをネットワーク経由で2回ダウンロードします。1つはdownloadTaksで、1回はData(cntentsOf :)でダウンロードします。ダウンロードタスクは単にネットワーク経由で実際にダウンロードしてデータを一時ファイルに書き込み、localUrl(この場合は場所)を渡すため、URLの代わりに場所を使用する必要があります。そのため、データはファイルからのみ読み取るように、ローカルURLを指す必要があります。
ステファン・デ・ルカ

使用例では、「ImageCacheLoader.obtainImageWithPath(imagePath:viewModel.image).......」と想定されていますか?
Tim Kruger、

非常に高速にスクロールすると機能しません。セルの再利用のため、画像は何度も入れ替わります。
Juan Boero

5

これは迅速なバージョンです(@Nitesh Borad目的Cコードを使用):-

   if let img: UIImage = UIImage(data: previewImg[indexPath.row]) {
                cell.cardPreview.image = img
            } else {
                // The image isn't cached, download the img data
                // We should perform this in a background thread
                let imgURL = NSURL(string: "webLink URL")
                let request: NSURLRequest = NSURLRequest(URL: imgURL!)
                let session = NSURLSession.sharedSession()
                let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
                    let error = error
                    let data = data
                    if error == nil {
                        // Convert the downloaded data in to a UIImage object
                        let image = UIImage(data: data!)
                        // Store the image in to our cache
                        self.previewImg[indexPath.row] = data!
                        // Update the cell
                        dispatch_async(dispatch_get_main_queue(), {
                            if let cell: YourTableViewCell = tableView.cellForRowAtIndexPath(indexPath) as? YourTableViewCell {
                                cell.cardPreview.image = image
                            }
                        })
                    } else {
                        cell.cardPreview.image = UIImage(named: "defaultImage")
                    }
                })
                task.resume()
            }

3

最善の答えはこれを行う正しい方法ではありません:(。実際にindexPathをモデルにバインドしましたが、これは必ずしも良いとは限りません。イメージのロード中にいくつかの行が追加されたと想像してください。指定されたindexPathのセルが画面上に存在しますが、イメージはもはや正しくありません!状況は少し起こりにくく、再現するのが困難ですが、それは可能です。

MVVMアプローチを使用して、コントローラーのセルをviewModelにバインドし、viewModelに画像をロードして(switchToLatestメソッドでReactiveCocoa信号を割り当て)、この信号をサブスクライブしてセルに画像を割り当てることをお勧めします!;)

MVVMを乱用しないように注意する必要があります。ビューは非常にシンプルでなければなりません!一方、ViewModelsは再利用可能である必要があります。コントローラーでView(UITableViewCell)とViewModelをバインドすることが非常に重要なのはそのためです。


1
はい、インデックスパスの「戦術的な修正」(これはお勧めしませんでしたが、OPの問題を解決するための最も控えめな編集でした)はこの問題の影響を受けます(ただし、テーブルビューで行が追加/削除され続ける場合のみ)。この現象が明らかになった場合は、他の方法で修正する可能性があります(同じインデックスパスを使用して検索するのではなく、適切な行のモデルをクエリするだけです)。しかし、その戦術的な修正には、ここで提起したものよりもさらに深刻な問題(上記で説明)があります。UIImageView私が助言するカテゴリソリューションを使用する場合、インデックスパスに関してそのような問題はありません。
Rob、

2
少し奇妙に聞こえるかもしれませんが、VIEWから任意の種類のロジックを呼び出すと、このアーキテクチャが悪用されます。
badeleux 14年

3

私の場合、それは画像のキャッシュ(使用されたSDWebImage)によるものではありませんでした。これは、カスタムセルのタグがindexPath.rowと一致しないためです。

cellForRowAtIndexPathで:

1)カスタムセルにインデックス値を割り当てます。例えば、

cell.tag = indexPath.row

2)メインスレッドで、画像を割り当てる前に、画像をタグと照合して、対応するセルに属しているかどうかを確認します。

dispatch_async(dispatch_get_main_queue(), ^{
   if(cell.tag == indexPath.row) {
     UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
     thumbnailImageView.image = tmpImage;
   }});
});

2

ありがとう「ロブ」.... UICollectionViewで同じ問題がありました。あなたの答えが私の問題の解決に役立ちました。これが私のコードです:

 if ([Dict valueForKey:@"ImageURL"] != [NSNull null])
    {
        cell.coverImageView.image = nil;
        cell.coverImageView.imageURL=nil;

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            if ([Dict valueForKey:@"ImageURL"] != [NSNull null] )
            {
                dispatch_async(dispatch_get_main_queue(), ^{

                    myCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];

                    if (updateCell)
                    {
                        cell.coverImageView.image = nil;
                        cell.coverImageView.imageURL=nil;

                        cell.coverImageView.imageURL=[NSURL URLWithString:[Dict valueForKey:@"ImageURL"]];

                    }
                    else
                    {
                        cell.coverImageView.image = nil;
                        cell.coverImageView.imageURL=nil;
                    }


                });
            }
        });

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

私にとって、mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];nilは決してないので、これは効果がありません。
カーボカチオン2014年

1
セルが表示されているかどうかを確認するには、次のようにします。for(mycell * updateCell in collectionView.visibleCells){cellVisible = YES; } if(cellVisible){cell.coverImageView.imageURL = [NSURL URLWithString:[Dict valueForKey:@ "ImageURL"]]; }私にも
有効

@snehaうん、あなたはそれをそのvisibleCellsように反復することによって見えることを確認することができますが、私[collectionView cellForItemAtIndexPath:indexPath]は使用がより効率的であると思います(そしてそれが最初にその呼び出しを行う理由です)。
ロブ

@snehaまた、ところで、上記のこの回答のコードサンプルでupdateCellnil、がでないかどうかを確認していますが、それを使用していません。これを使用して、コレクションビューのセルがまだ表示されているかどうかを判断するだけでなく、updateCellこのブロック内では使用しないでくださいcell(これはもう有効ではない可能性があります)。そして、もしそうであればnil、何もする必要はありません(このセルが表示されていないため)。
ロブ

2
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
        MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

        cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (data) {
                UIImage *image = [UIImage imageWithData:data];
                if (image) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                        if (updateCell)
                            updateCell.poster.image = image;
                    });
                }
            }
        }];
        [task resume];

        return cell;
    }

0

バックグラウンドでのセルの画像読み込み時に、セル読み込みを高速化したいと思います。そのために、次の手順を実行しました。

  1. ファイルがドキュメントディレクトリに存在するかどうかを確認します。

  2. そうでない場合は、画像を初めてロードし、電話のドキュメントディレクトリに保存します。携帯電話に画像を保存したくない場合は、セルの画像をバックグラウンドで直接ロードできます。

  3. 読み込みプロセス:

含めるだけ: #import "ManabImageOperations.h"

セルのコードは次のようになります。

NSString *imagestr=[NSString stringWithFormat:@"http://www.yourlink.com/%@",[dictn objectForKey:@"member_image"]];

        NSString *docDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
        NSLog(@"Doc Dir: %@",docDir);

        NSString  *pngFilePath = [NSString stringWithFormat:@"%@/%@",docDir,[dictn objectForKey:@"member_image"]];

        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pngFilePath];
        if (fileExists)
        {
            [cell1.memberimage setImage:[UIImage imageWithContentsOfFile:pngFilePath] forState:UIControlStateNormal];
        }
        else
        {
            [ManabImageOperations processImageDataWithURLString:imagestr andBlock:^(NSData *imageData)
             {
                 [cell1.memberimage setImage:[[UIImage alloc]initWithData: imageData] forState:UIControlStateNormal];
                [imageData writeToFile:pngFilePath atomically:YES];
             }];
}

ManabImageOperations.h:

#import <Foundation/Foundation.h>

    @interface ManabImageOperations : NSObject
    {
    }
    + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage;
    @end

ManabImageOperations.m:

#import "ManabImageOperations.h"
#import <QuartzCore/QuartzCore.h>
@implementation ManabImageOperations

+ (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage
{
    NSURL *url = [NSURL URLWithString:urlString];

    dispatch_queue_t callerQueue = dispatch_get_main_queue();
    dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL);
    dispatch_async(downloadQueue, ^{
        NSData * imageData = [NSData dataWithContentsOfURL:url];

        dispatch_async(callerQueue, ^{
            processImage(imageData);
        });
    });
  //  downloadQueue=nil;
    dispatch_release(downloadQueue);

}
@end

問題が発生した場合は、回答を確認してコメントしてください。


0

単に変更し、

dispatch_async(kBgQueue, ^{
     NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
     dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
     });
 });

    dispatch_async(kBgQueue, ^{
         NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
         cell.poster.image = [UIImage imageWithData:imgData];
         dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
         });
     });

0

URLを渡すだけで、

NSURL *url = [NSURL URLWithString:@"http://www.myurl.com/1.png"];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data,    NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (data) {
        UIImage *image = [UIImage imageWithData:data];
        if (image) {
            dispatch_async(dispatch_get_main_queue(), ^{
                    yourimageview.image = image;
            });
        }
    }
}];
[task resume];

理由を教えてもらえますか?
User558、2017

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

    If (cell == nil)
    {

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"QTStaffViewCell" owner:self options:nil];
        cell = [nib objectAtIndex: 0];

    }

    StaffData = [self.staffArray objectAtIndex:indexPath.row];
    NSString *title = StaffData.title;
    NSString *fName = StaffData.firstname;
    NSString *lName = StaffData.lastname;

    UIFont *FedSanDemi = [UIFont fontWithName:@"Aller" size:18];
    cell.drName.text = [NSString stringWithFormat:@"%@ %@ %@", title,fName,lName];
    [cell.drName setFont:FedSanDemi];

    UIFont *aller = [UIFont fontWithName:@"Aller" size:14];
    cell.drJob.text = StaffData.job;
    [cell.drJob setFont:aller];

    if ([StaffData.title isEqualToString:@"Dr"])
    {
        cell.drJob.frame = CGRectMake(83, 26, 227, 40);
    }
    else
    {
        cell.drJob.frame = CGRectMake(90, 26, 227, 40);

    }

    if ([StaffData.staffPhoto isKindOfClass:[NSString class]])
    {
        NSURL *url = [NSURL URLWithString:StaffData.staffPhoto];
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url
                completionHandler:^(NSURL *location,NSURLResponse *response, NSError *error) {

      NSData *imageData = [NSData dataWithContentsOfURL:location];
      UIImage *image = [UIImage imageWithData:imageData];

      dispatch_sync(dispatch_get_main_queue(),
             ^{
                    cell.imageView.image = image;
              });
    }];
        [task resume];
    }
       return cell;}

2
説明のないコードダンプが役立つことはほとんどありません。コンテキストを提供するためにこの回答を編集することを検討してください。
Chris
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.