Objective-C
iPhone
Xcode
iOS

URLから画像を非同期に読み込む3種類の実装

More than 5 years have passed since last update.

URLから画像を生成するときは、普通に実装すると以下のようになります。


ViewController.m

NSURL *url = [NSURL URLWithString:@"画像のURL"];

NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

しかし、この実装だとシングルスレッドで読み込むので、その間他の処理が止まり固まったように見えてしまいます。

なので、画像は非同期に読み込むことが必要になります。今回は非同期での画像読み込みの実装を3種類してみました。

3つの実装をまとめたソースコードは、こちらからダウンロードしてください。


  1. 一度読み込んだ画像をキャッシュしない。(UITableViewCellの画像)

  2. 一度読み込んだ画像をキャッシュして、再び読み込まない。(UITableViewCellの画像)

  3. UIImageViewのインスタンスに読み込み処理を任せて、それぞれで読み込む。(UIImageView)


1. 一度読み込んだ画像をキャッシュしない。

参考URL: ネット上の画像を表示させた UITableView をぬるぬる動作させる方法


UncacheAsyncTableController.m

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

{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

// この部分が重要
dispatch_queue_t q_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t q_main = dispatch_get_main_queue();
cell.imageView.image = nil;
dispatch_async(q_global, ^{
NSString *imageURL = @"http://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Apple_Computer_Logo_rainbow.svg/150px-Apple_Computer_Logo_rainbow.svg.png";
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL]]];

dispatch_async(q_main, ^{
cell.imageView.image = image;
[cell layoutSubviews];
});
});

return cell;
}



2. 一度読み込んだ画像をキャッシュして、再び読み込まない。

参考URL: UITableViewの要素を非同期に設定する

以下のクラスを組み込んで下さい。


  • Downloader.h (非ARC)

  • Downloader.m (非ARC)


CacheAsyncTableController.h

#import "Downloader.h"


@interface CacheAsyncTableController : UITableViewController

@property (nonatomic, strong) NSMutableDictionary *imageCache;
@property (nonatomic, strong) NSMutableDictionary *downloaderManager;

@end



CacheAsyncTableController.m

- (void)viewDidLoad

{
[super viewDidLoad];

// 非同期表示・キャッシュ用の配列の初期化
self.imageCache = [NSMutableDictionary dictionary];
self.downloaderManager = [NSMutableDictionary dictionary];
}

***(略)***

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

if ([_imageCache objectForKey:indexPath]) {
// すでにキャッシュしてある場合
cell.imageView.image = [_imageCache objectForKey:indexPath];
} else {
if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
{
// キャッシュがない場合、読み込む
[self startIconDownload:indexPath];
}
}

return cell;
}

***(略)***

// 以下のメソッドを実装する(ソースコード参照)
- (void)startIconDownload:(NSIndexPath *)indexPath;
- (void)loadImagesForOnscreenRows;
- (void)downloader:(NSURLConnection *)conn didLoad:(NSMutableData *)data identifier:(id)identifier;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;



3. UIImageViewのインスタンスに読み込み処理を任せて、それぞれで読み込む。

参考URL: 非同期通信で画像をロードする方法について

以下のクラスを組み込んで下さい。


  • AsyncImageView.h

  • AsyncImageView.m

UIImageViewを生成するところで、以下のように書きます。


AsyncImageViewController.m

AsyncImageView *imageView = [[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];

NSString *imageURL = @"http://upload.wikimedia.org/wikipedia/commons/a/a5/Apple_gray_logo.png";
[imageView loadImage:imageURL]; // 画像を非同期で読み込む