読者です 読者をやめる 読者になる 読者になる

プログラミングノート

一からものを作ることが好きなエンジニアの開発ブログです。

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

iOS

UITableViewなどのスクロール系のViewでサムネイル画像を出す場合、下記のような記述ではメインスレッドで画像がロードされるため、ユーザーのUI操作に影響が出てしまいます。

NSURL *url = [NSURL URLWithString:@"URL"];
NSData *data = [NSData dataWithContentsOfURL:url];
[instanceOfUIImageView setImage:[[UIImage alloc] initWithData:data]];


こういう場合は非同期通信を行うのが定石だと思いますが、参考になるサンプルがあまり無かったので試しに作ってみました。UIImageViewを継承したUIAsyncImageViewクラスです。

UIAsyncImageView.h

画像URLの指定と、通信中止用のメソッドのみ定義しています。

#import <Foundation/Foundation.h>
@interface UIAsyncImageView : UIImageView {
@private
  NSURLConnection *conn;
  NSMutableData *data;
}
-(void)loadImage:(NSString *)url;
-(void)abort;
@end
UIAsyncImageView.m

NSURLConnectionによる非同期通信で画像データを取得しています。
connectionXXX系のメソッドは上記クラスのデリゲートメソッドです。

#import "UIAsyncImageView.h"
@implementation UIAsyncImageView

-(void)loadImage:(NSString *)url{
  [self abort];
  self.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.2];
  data = [[NSMutableData alloc] initWithCapacity:0];

  NSURLRequest *req = [NSURLRequest 
                       requestWithURL:[NSURL URLWithString:url]
                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                       timeoutInterval:30.0];
  conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
  [data setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)nsdata{
  [data appendData:nsdata];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
  [self abort];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
  self.image = [UIImage imageWithData:data];
  self.contentMode = UIViewContentModeScaleAspectFit;
  self.autoresizingMask = UIViewAutoresizingFlexibleWidth || UIViewAutoresizingFlexibleHeight;
  [self abort];
}

-(void)abort{
  if(conn != nil){
    [conn cancel];
    [conn release];
    conn = nil;
  }
  if(data != nil){
    [data release];
    data = nil;
  }
}

- (void)dealloc {
  [conn cancel];
  [conn release];
  [data release];
  [super dealloc];
}
@end
利用方法

UIImageViewと同様です。
loadImageを呼び出すと通信が開始され、データ取得完了した段階で画面に表示されます。

UIAsyncImageView *ai = [[UIAsyncImageView alloc] initWithFrame:CGRectMake(0,0,50,50)];
[ai loadImage:@"URL"];
[self.view addSubview:ai];
Flickrサンプル

FlickrAPIを使ってUIScrollViewに表示するサンプルです。UITableViewではないですが、実機でもスムーズに動作しています。角丸にするライブラリも発見したので入れてみました。



コードはこちらから

その他

UITableViewへ画像を表示する場合は、少し工夫がいるのでこれだけでは上手くいきません。iOS Dev Centerにある、LazyTableImagesというサンプルが参考になります。