Cache URL images iphone UITableview
Asked Answered
T

5

11

I'm seeking a tutorial on how to cache images loaded from a url into cells of a uitableview.

I found an example here

http://www.ericd.net/2009/05/iphone-caching-images-in-memory.html#top

But the code is incomplete. I'm an objective c novice so I found it very difficult to fill in the missing pieces.

Terribly answered 15/2, 2010 at 10:5 Comment(1)
Instead of implementing everything yourself, you could use a helper like UIImageLoader (github.com/gngrwzrd/UIImageLoader) or SDWebImage. UIImageLoader is only two files and super simple to use. There's a great sample in the repo as well.Pennant
T
8

A nice working example was found here

http://ezekiel.vancouver.wsu.edu/~wayne/yellowjacket/YellowJacket.zip

Terribly answered 15/2, 2010 at 11:39 Comment(1)
Updated version for iOS 5: git clone http://ezekiel.vancouver.wsu.edu/~cs458/demos/YellowJackets/.git linkSchaub
M
35

Here is a simple ImageCache implementation using NSCache. ImageCache is a singelton.

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

Example

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];
    }
Mckie answered 3/2, 2012 at 18:17 Comment(7)
in ImageCache.m the method there's a typo: - (void) AddImage:(NSString *)imageURL: (NSString *)image should be - (void) AddImage:(NSString *)imageURL: (UIImage *)image. The (NSString *)image should be (UIImage *)image.Acne
Thanks for your great answer. It is full fill with req. informationHooten
Really helpful answer +1, 1 question where is this cache images are stored i have checked document directory that does not contain it and other question is how could this code will delete this cache images or no need to do that?Hedva
Well done. But I'd suggest 1. following camelCase naming conventions for your methods; and 2. use dispatch_once for instantiating your singleton (see Matt Galloway's singleton discussion.Leoine
Hi, I do the same as you did here, but it is not working. I want to show the thumbnail image in customCell with NSCache. Here is my post, #19253491 please take have a look on it. Thanks..Chapell
Good one. But once there are a lot of images in the cache, it will cause a memory warning and might eventually crash. Why don't you add an observer for memoryWarning: in init and remove cache objects if the method is fired.Cacoepy
@GautamJain can you explain a bit more about this? Can you give the code to do that for this example?Nahama
T
8

A nice working example was found here

http://ezekiel.vancouver.wsu.edu/~wayne/yellowjacket/YellowJacket.zip

Terribly answered 15/2, 2010 at 11:39 Comment(1)
Updated version for iOS 5: git clone http://ezekiel.vancouver.wsu.edu/~cs458/demos/YellowJackets/.git linkSchaub
U
2

You could also try using the awesome EgoImage library written by the sharp fellows at enormego to accomplish this. It is very simple to use, makes efficient use of cache behind the scenes and is ideally suited to meet your requirements.

Here's the github path for the library which includes a demo app.

Underfoot answered 17/8, 2011 at 6:56 Comment(0)
A
0

I wrote this (with concepts and some code taken from Lane Roathe's excellent UIImageView+Cache category) for an app I've been working on. It uses the ASIHTTPRequest classes as well, which are great. This could definitely be improved.. for example, by allowing requests to be canceled if no longer needed, or by utilizing the notification userInfo to allow for more precise UI updating.. but it's working well for my purposes.

@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];
}

You call this code as follows:

[ImageFetcher asyncImageFetch:&icon withURL:url];

I'm also using notifications, for better or worse, to let any owners of the corresponding UIImage know when they should redisplay- in this case, it's in a tableView context:

- (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];
        // ...
}
Amador answered 19/11, 2010 at 16:2 Comment(1)
An additional tip here: if you are caching lots of images, or large images, and are therefore using a good amount of memory, you may want to make a "+ (void)flushCache" method that does "[cache removeAllObjects]" so you can call it from within the didReceiveMemoryWarning methods as needed.. that will free up all of that memory.Amador
C
0

You also might wanna check HJCache. It comes with a UIImageView compatible view class that does all the caching transparently and is suitable to be used in UITableViewCells where scrolling performance is important.

Catbird answered 6/12, 2011 at 10:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.