AdamM
AdamM

Reputation: 4440

Storing and retrieving images on iPhone efficiently

I have an application which donwloads several images and stores them on the phone. In total it will probably required around 20 images tops. I need to be able to retrieve any of these images at will depending on what screen the user is on. These images will be stored indefinitely, so I don't want to use temp directory.

At present I have a class named Images with these methods

- (void) cacheImage: (NSString *) ImageURLString : (NSString *)imageName
{
    NSURL *ImageURL = [NSURL URLWithString: ImageURLString];

    // Generate a unique path to a resource representing the image you want

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = [paths objectAtIndex: 0];
    NSString *docFile = [docDir stringByAppendingPathComponent: imageName];

    // Check for file existence
    if(![[NSFileManager defaultManager] fileExistsAtPath: docFile])
    {
        // The file doesn't exist, we should get a copy of it

        // Fetch image
        NSData *data = [[NSData alloc] initWithContentsOfURL: ImageURL];

        UIImage *image = [[UIImage alloc] initWithData: data];

        // Is it PNG or JPG/JPEG?
        // Running the image representation function writes the data from the image to a file
        if([ImageURLString rangeOfString: @".png" options: NSCaseInsensitiveSearch].location != NSNotFound)
        {

            [UIImagePNGRepresentation(image) writeToFile: docFile atomically: YES];

        }
        else if([ImageURLString rangeOfString: @".jpg" options: NSCaseInsensitiveSearch].location != NSNotFound || 
                [ImageURLString rangeOfString: @".jpeg" options: NSCaseInsensitiveSearch].location != NSNotFound)
        {
            [UIImageJPEGRepresentation(image, 100) writeToFile: docFile atomically: YES];
        }
    }
}

- (UIImage *) getCachedImage : (NSString *)imageName
{

    NSArray *paths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString* cachedPath = [documentsDirectory stringByAppendingPathComponent:imageName];

    UIImage *image;

    // Check for a cached version
    if([[NSFileManager defaultManager] fileExistsAtPath: cachedPath])
    {
        image = [UIImage imageWithContentsOfFile: cachedPath]; // this is the cached image
    }
    else
    {
        NSLog(@"Error getting image %@", imageName);
    }

    return image;
}

-(void)getImages
{    

//example
    NSString *image1URL = @"http://test/image1.png";        
    NSString *image2URL = @"http://test/image2.png";        
    NSString *image3URL = @"http://test/image3.png";

    [self cacheImage:sLogo: @"Image1"];
    [self cacheImage:sBlankNav: @"Image2"];
    [self cacheImage:buttonLarge :@"Image3"];

}

-(void) storeImages
{
    image1 = [self getCachedImage:@"Image1"];
    image2 = [self getCachedImage:@"Image2"];
    image3 = [self getCachedImage:@"Image3"];

}

So I use the code like this

Images *cache = [[Images alloc]init];
[cache storeImages];

The get images method is called once when the app first starts to get the images, it isn't called again after that, unless the images on the server are updated and I need to retrieve the updated ones.

The code works, but the problem is when I navigate to a screen that uses it, there is a very slight delay before the screen loads as it is loading the images.

My application is a tabbed application, so it begins on tab 1, I click tab 2 which implements the code, there will be a slight pause the first time it loads. It doesn't last very long, but it is noticeable and is very annoying. After that it is fine, as it is already loaded. However with navigation controller, every time you move from the first VC to the second VC, the method will be called again, so each time you navigate the delay will be there.

The images are not very big, biggest one is 68kb, others are much smaller than that. At present I am just testing with 5 images. Is there a more efficient way of storing and retrieving images, or am I doing something wrong with my code? I need to be able to retrieve these images without any noticeable delay in order for my application to remain fluid and not jerky or clunky.

Thanks in advance!!

Upvotes: 0

Views: 1033

Answers (2)

Echilon
Echilon

Reputation: 10264

You have two options to do the image loading work on a background thread - use Grand Central Dispatch or NSInvocationOperation. GCD might be considered the cleaner of the two:

dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(q, ^{
        //load images here
        dispatch_async(main, ^{
          // show on main thread here
        });
});

Upvotes: 1

Mr Bonjour
Mr Bonjour

Reputation: 3400

you have delay because you're downloading data synchronously

//  NSData *data = [[NSData alloc] initWithContentsOfURL: ImageURL];

Try some smart library like SDWebImage: it lets you download image asynchronously while you still can display a local image (a proxy image). By the way, you still get cache image for free. So even if u are on local, you can still catch previously downloaded images

https://github.com/rs/SDWebImage

A must have

Upvotes: 1

Related Questions