URLから読み込まれた画像をuitableviewのセルにキャッシュする方法に関するチュートリアルを探しています。
ここで例を見つけました
http://www.ericd.net/2009/05/iphone-caching-images-in-memory.html#top
しかし、コードは不完全です。私は客観的な初心者なので、欠けている部分を埋めるのは非常に難しいと感じました。
URLから読み込まれた画像をuitableviewのセルにキャッシュする方法に関するチュートリアルを探しています。
ここで例を見つけました
http://www.ericd.net/2009/05/iphone-caching-images-in-memory.html#top
しかし、コードは不完全です。私は客観的な初心者なので、欠けている部分を埋めるのは非常に難しいと感じました。
これは、NSCache を使用した単純な ImageCache の実装です。ImageCache はシングルトンです。
ImageCache.h
#import <Foundation/Foundation.h>
@interface ImageCache : NSObject
@property (nonatomic, retain) NSCache *imgCache;
#pragma mark - Methods
+ (ImageCache*)sharedImageCache;
//- (void) AddImage:(NSString *)imageURL: (UIImage *)image;
- (void) AddImage:(NSString *)imageURL withImage:(UIImage *)image;
- (UIImage*) GetImage:(NSString *)imageURL;
- (BOOL) DoesExist:(NSString *)imageURL;
@end
ImageCache.m
#import "ImageCache.h"
@implementation ImageCache
@synthesize imgCache;
#pragma mark - Methods
static ImageCache* sharedImageCache = nil;
+(ImageCache*)sharedImageCache
{
@synchronized([ImageCache class])
{
if (!sharedImageCache)
sharedImageCache= [[self alloc] init];
return sharedImageCache;
}
return nil;
}
+(id)alloc
{
@synchronized([ImageCache class])
{
NSAssert(sharedImageCache == nil, @"Attempted to allocate a second instance of a singleton.");
sharedImageCache = [super alloc];
return sharedImageCache;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil)
{
imgCache = [[NSCache alloc] init];
}
return self;
}
// - (void) AddImage:(NSString *)imageURL: (UIImage *)image
- (void) AddImage:(NSString *)imageURL withImage:(UIImage *)image
{
[imgCache setObject:image forKey:imageURL];
}
- (NSString*) GetImage:(NSString *)imageURL
{
return [imgCache objectForKey:imageURL];
}
- (BOOL) DoesExist:(NSString *)imageURL
{
if ([imgCache objectForKey:imageURL] == nil)
{
return false;
}
return true;
}
@end
例
UIImage *image;
// 1. Check the image cache to see if the image already exists. If so, then use it. If not, then download it.
if ([[ImageCache sharedImageCache] DoesExist:imgUrl] == true)
{
image = [[ImageCache sharedImageCache] GetImage:imgUrl];
}
else
{
NSData *imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: imgUrl]];
image = [[UIImage alloc] initWithData:imageData];
// Add the image to the cache
//[[ImageCache sharedImageCache] AddImage:imgUrl :image];
[[ImageCache sharedImageCache] AddImage:imgUrl withImage:image];
}
これを達成するために、 enormegoの鋭い仲間によって書かれた素晴らしい EgoImage ライブラリを使用することもできます。使い方は非常に簡単で、バックグラウンドでキャッシュを効率的に使用できるため、要件を満たすのに最適です。
デモ アプリを含むライブラリのgithub パスは次のとおりです。
また、 HJCacheを確認することもできます。すべてのキャッシュを透過的に実行するUIImageView互換のビュークラスが付属しており、スクロールパフォーマンスが重要なUITableViewCellsでの使用に適しています。
これは、私が取り組んでいるアプリのために (Lane Roathe の優れた UIImageView+Cache カテゴリから概念といくつかのコードを使用して) 書きました。ASIHTTPRequestクラスも使用していますが、これは素晴らしいことです。これは間違いなく改善される可能性があります..たとえば、不要になった場合にリクエストをキャンセルできるようにするか、通知 userInfo を利用してより正確な UI 更新を可能にすることで..しかし、私の目的にはうまく機能しています。
@implementation ImageFetcher
#define MAX_CACHED_IMAGES 20
static NSMutableDictionary* cache = nil;
+ (void)asyncImageFetch:(UIImage**)anImagePtr withURL:(NSURL*)aUrl {
if(!cache) {
cache = [[NSMutableDictionary dictionaryWithCapacity:MAX_CACHED_IMAGES] retain];
}
UIImage* newImage = [cache objectForKey:aUrl.description];
if(!newImage) { // cache miss - doh!
ASIHTTPRequest *imageRequest = [ASIHTTPRequest requestWithURL:aUrl];
imageRequest.userInfo = [NSDictionary dictionaryWithObject:[NSValue valueWithPointer:anImagePtr] forKey:@"imagePtr"];
imageRequest.delegate = self;
[imageRequest setDidFinishSelector:@selector(didReceiveImage:)];
[imageRequest setDidFailSelector:@selector(didNotReceiveImage:)];
[imageRequest startAsynchronous];
}
else { // cache hit - good!
*anImagePtr = [newImage retain];
}
}
+ (void)didReceiveImage:(ASIHTTPRequest *)request {
NSLog(@"Image data received.");
UIImage **anImagePtr = [(NSValue*)[request.userInfo objectForKey:@"imagePtr"] pointerValue];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *newImage = [[UIImage imageWithData:[request responseData]] retain];
if(!newImage) {
NSLog(@"UIImageView: LoadImage Failed");
}
else {
*anImagePtr = newImage;
// check to see if we should flush existing cached items before adding this new item
if( [cache count] >= MAX_CACHED_IMAGES)
[cache removeAllObjects];
[cache setValue:newImage forKey:[request url].description];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName: @"ImageDidLoad" object: self userInfo:request.userInfo];
}
[pool drain];
}
このコードを次のように呼び出します。
[ImageFetcher asyncImageFetch:&icon withURL:url];
また、良くも悪くも通知を使用して、対応する UIImage の所有者にいつ再表示する必要があるかを知らせます。この場合は、tableView コンテキストにあります。
- (void)viewDidLoad {
[super viewDidLoad];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(imageDidLoad:) name:@"ImageDidLoad" object:nil];
}
- (void)imageDidLoad:(NSNotification*)notif {
NSLog(@"Received icon load notification.");
// reload table view so that new image appears.. would be better if I could
// only reload the particular UIImageView that holds this image, oh well...
[self.tableView reloadData];
}
- (void)dealloc {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
// ...
}