shajem
shajem

Reputation: 2121

Download image asynchronously

I need to download an image from the web and display it in an ImageView. Presently I am using SDWebImage (It is an asynchronous image downloader with cache support, with a UIImageView category).

But it crashes when I click the back button and forward button (when I try to go back and forth of the view repeatedly). Anyway this happens very rarely, but I need to get rid of this bug. Is there any other library (that does not use private API's) that I could use in my project?

Upvotes: 4

Views: 8314

Answers (8)

Simon
Simon

Reputation: 791

I know this is a very old thread but recently i had a lot of random crashes with SDWebImage, so i had to implement my own lazy loading and caching mechanism. It works pretty well, i just haven't tested it in heavy load cases. So here is the .h and .m files followed by the way i use it :

//  UIImageView+CustomCache.h
@interface UIImageView(CustomCache)

-(void)startAsyncDownload:(UIImage*)placeHolderImage imageUrlString:(NSString*)imageUrlString;
@end


//  UIImageView+CustomCache.m

#import "UIImageView+CustomCache.h"

@implementation UIImageView(CustomCache)

-(void)startAsyncDownload:(UIImage*)placeHolderImage imageUrlString:(NSString*)imageUrlString{
    self.image = placeHolderImage;
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:
                                              [NSURL URLWithString:imageUrlString]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionHandler){
        @autoreleasepool {
            if (connectionHandler != nil) {
                NSLog(@"error in downloading description %@",connectionHandler.localizedDescription);
            } else {
                ImagesCacheHandler *ref = [ImagesCacheHandler new];
                UIImage *imageFromData = [[UIImage alloc] initWithData:data];
                if (imageFromData != NULL && imageFromData != nil && data.length > 0) {
                    self.image = imageFromData;
//custom store to sqlite
                    [ref archiveImage:imageUrlString imageData:data moc:[ref fetchContext]];
                }
            }
        }
    }];
}

@end

And in my table view i use (i import of course UIImageView+CustomCache.h)

UIImageView *imageViewToLazyLoad = (UIImageView*)[cell viewWithTag:1];
[imageViewToLazyLoad startAsyncDownload:[UIImage imageNamed@"palce_Holder_Image_name"] imageUrlString:imageUrl];

Upvotes: 1

rohan-patel
rohan-patel

Reputation: 5782

Yes. You can user other libary. I've already implemented that using AsyncImageView which is inherited from UIImageView. What it does is it stores images in Cache memory fetched from a url and whenever you need to load image from the same URL again it will simply load it from the cache memory saving a lot of time.

Just follow the link for implementing that:

https://github.com/nicklockwood/AsyncImageView#readme

http://www.markj.net/iphone-asynchronous-table-image/

Please have a look at the image showing the technique I've implemented. It lets you do other activity while images are loading.:

enter image description here

Upvotes: 10

Kuldeep
Kuldeep

Reputation: 2589

//JImage.h

#import <Foundation/Foundation.h>


@interface JImage : UIImageView {

    NSURLConnection *connection;

    NSMutableData* data;

    UIActivityIndicatorView *ai;
}

-(void)initWithImageAtURL:(NSURL*)url;  

@property (nonatomic, retain) NSURLConnection *connection;

@property (nonatomic, retain) NSMutableData* data;

@property (nonatomic, retain) UIActivityIndicatorView *ai;

@end



//JImage.m

#import "JImage.h"

@implementation JImage
@synthesize ai,connection, data;

-(void)initWithImageAtURL:(NSURL*)url {


    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    [self setContentMode:UIViewContentModeScaleToFill];

    if (!ai){

        [self setAi:[[UIActivityIndicatorView alloc]   initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]]; 

        [ai startAnimating];

        [ai setFrame:CGRectMake(27.5, 27.5, 20, 20)];

        [ai setColor:[UIColor blackColor]];

        [self addSubview:ai];
    }
    NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];

    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];    
}


- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {

if (data==nil) data = [[NSMutableData alloc] initWithCapacity:5000];

[data appendData:incrementalData];

NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[data length]];

NSLog(@"resourceData length: %d", [resourceLength intValue]);

}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{
    NSLog(@"Connection error...");

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    [ai removeFromSuperview];

}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection 
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    [self setImage:[UIImage imageWithData: data]];

    [ai removeFromSuperview];   
}
@end



//Include the definition in your class where you want to use the image
-(UIImageView*)downloadImage:(NSURL*)url:(CGRect)frame {

JImage *photoImage=[[JImage alloc] init]; 

    photoImage.backgroundColor = [UIColor clearColor]; 

   [photoImage setFrame:frame];

   [photoImage setContentMode:UIViewContentModeScaleToFill]; 

    [photoImage initWithImageAtURL:url];

    return photoImage;
}


//How to call the class

UIImageView *imagV=[self downloadImage:url :rect]; 

//you can call the downloadImage function in looping statement and subview the returned  imageview. 
//it will help you in lazy loading of images.


//Hope this will help

Upvotes: 2

Stanley Tang
Stanley Tang

Reputation: 256

I personally use the built in Grand Central Dispatch feature in iOS to download images from the server asynchronously.

Below is a code I used to fetch photos from Flickr in one of my apps.

In your image/photo class, have a function that is something like this:

- (void)processImageDataWithBlock:(void (^)(NSData *imageData))processImage
{
NSString *url = self.imageURL;
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_queue_t downloadQueue = dispatch_queue_create("Photo Downloader", NULL);
dispatch_async(downloadQueue, ^{
    NSData *imageData = *insert code that fetches photo from server*;
    dispatch_async(callerQueue, ^{
        processImage(imageData);
    });
});
dispatch_release(downloadQueue);
}

In your Photo View Controller, you can call this function like this:

- (void)viewWillAppear:(BOOL)animated
{
[spinner startAnimating];
[self.photo processImageDataWithBlock:^(NSData *imageData) {
    if (self.view.window) {
        UIImage *image = [UIImage imageWithData:imageData];
        imageView.image = image;
        imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
        scrollView.contentSize = image.size;
        [spinner stopAnimating];
    }
}];
}

Upvotes: 4

Manish Agrawal
Manish Agrawal

Reputation: 11026

Check out EGOImageLoading by enormego for caching images. It Works just like UIImageView and lets you download images from HTTP asynchronously and also its easy to integrate

Upvotes: 2

axe
axe

Reputation: 27

I personally prefer using NSURLConnection sendSynchronousRequest and putting a GCD wrapper around it. Keeps everything neat and tidy.

Upvotes: 0

LuisEspinoza
LuisEspinoza

Reputation: 8538

I think that the bug that you describe may occur because when you "go back" release some objects that can be delegates of connections that are still running. For avoid crashes, you should cancel the connections before release or dealloc any object that could be a delegate of a running connection.

Another alternative for image async download is http://allseeing-i.com/ASIHTTPRequest/ .

Upvotes: 4

rickster
rickster

Reputation: 126107

NSURLConnection provides asynchronous downloading and is built into iOS.

Upvotes: 6

Related Questions