Powered By Blogger

Tuesday, July 10, 2012

lazy loading in iphone

Hi,
Here I'm placing code for LazyLoading of Images in iPhone/iPad/iOS Application. Please go through it. It'll surely help you.

Step 1: Create  "DTLazyImageView.h"

@class DTLazyImageView;

@protocol DTLazyImageViewDelegate <NSObject>
@optional
- (void)lazyImageView:(DTLazyImageView *)lazyImageView didChangeImageSize:(CGSize)size;
- (void)imageDidLoad: (DTLazyImageView *) lazyImageView;
@end

@interface DTLazyImageView : UIImageView
{
    UIActivityIndicatorView *actv;
}
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, assign) BOOL shouldShowProgressiveDownload;
@property (nonatomic, strong) UIActivityIndicatorView *actv;
@property (nonatomic, assign) id<DTLazyImageViewDelegate> delegate;    // subtle simulator bug - use assign not __unsafe_unretained

- (void)startLoading;
- (void)cancelLoading;
- (void)loadImageAtURL:(NSURL *)url;
@end

Step 2: Create  "DTLazyImageView.m" file.


#import "DTLazyImageView.h"

static NSCache *_imageCache = nil;

@interface DTLazyImageView ()

- (void)notify;

@end

@implementation DTLazyImageView
{
    NSURL *_url;
   
    NSURLConnection *_connection;
    NSMutableData *_receivedData;

    CGFloat _fullHeight;
    CGFloat _fullWidth;
    NSUInteger _expectedSize;
   
    BOOL shouldShowProgressiveDownload;
   
    __unsafe_unretained id<DTLazyImageViewDelegate> _delegate;
}
@synthesize delegate=_delegate;

- (void)dealloc
{   
    [_connection cancel];
    actv = nil;
}

- (void) startLoading
{
    NSLog(@"Tag: %d , Url :%@",self.tag,[_url absoluteString]);
   
    if (!self.image && [[_url absoluteString] length]>0)
    {
        UIImage *image = [_imageCache objectForKey:_url];
        actv = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        actv.frame = CGRectMake(50.0, 50.0, 20.0, 20.0);
        [self addSubview:actv];
        if (image)
        {
            self.image = image;
            _fullWidth = image.size.width;
            _fullHeight = image.size.height;
           
            // for unknown reasons direct notify does not work below iOS 5
            [self performSelector:@selector(notify) withObject:nil afterDelay:0.0];
            return;
        }
        [self loadImageAtURL:_url];
    }   
    else
    {
       UIImage *image = [UIImage imageNamed:@"logo.png"];
       self.image = image;
        _fullWidth = image.size.width;
        _fullHeight = image.size.height;
    }
}

- (void)loadImageAtURL:(NSURL *)url
{
    [self performSelectorInBackground:@selector(loadImage:) withObject:url];
}

- (void)loadImage:(NSURL *)url
{
    @autoreleasepool
    {
        [actv startAnimating];
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
       
        _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
        [_connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
        [_connection start];
       
        // necessary because otherwise otherwise the delegate methods would not get delivered
        CFRunLoopRun();
    }
}


- (void)didMoveToSuperview
{
    [super didMoveToSuperview];
}

- (void)cancelLoading
{
    [_connection cancel];
    _connection = nil;
    _receivedData = nil;
}

#pragma mark NSURL Loading

- (void)notify
{
    [self.delegate imageDidLoad:self];
}

- (void)completeDownloadWithData:(NSData *)data
{
    UIImage *image = [[UIImage alloc] initWithData:data];
    self.image = image;
    _fullWidth = image.size.width;
    _fullHeight = image.size.height;
   
    [self notify];
    [actv stopAnimating];
    [actv removeFromSuperview];
   
    static dispatch_once_t predicate;
   
    dispatch_once(&predicate, ^{
        _imageCache = [[NSCache alloc] init];
    });
   
    if (_url)
    {
        // cache image
        [_imageCache setObject:image forKey:_url];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    // every time we get an response it might be a forward, so we discard what data we have
    _receivedData = nil;
   
    // does not fire for local file URLs
    if ([response isKindOfClass:[NSHTTPURLResponse class]])
    {
        NSHTTPURLResponse *httpResponse = (id)response;
       
        if (![[httpResponse MIMEType] hasPrefix:@"image"])
        {
            [self cancelLoading];
            return;
        }
    }
   
    /* For progressive download */
    _fullWidth = _fullHeight = -1.0f;
    _expectedSize = (NSUInteger)[response expectedContentLength];
   
    _receivedData = [[NSMutableData alloc] init];
}

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

- (void)removeFromSuperview
{
    [super removeFromSuperview];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if (_receivedData)
    {
        [self performSelectorOnMainThread:@selector(completeDownloadWithData:) withObject:_receivedData waitUntilDone:YES];
        _receivedData = nil;
    }
    _connection = nil;
    CFRunLoopStop(CFRunLoopGetCurrent());
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    _connection = nil;
    _receivedData = nil;
    CFRunLoopStop(CFRunLoopGetCurrent());
}

#pragma mark Properties

@synthesize url = _url;
@synthesize shouldShowProgressiveDownload;
@synthesize actv;
@end

Step 3:  DO the following steps to your current controller where you want to use LazyImageLoad.

#import "DTLazyImageView.h"

@interface RootViewController : UIViewController<DTLazyImageViewDelegate> {
   
    DTLazyImageView *lazyImageView;
}

@end

@implementation RootViewController

-(void)viewWillAppear:(BOOL)animated
{
    for(int i=0;i<5;i++)
    {
                DTLazyImageView *lazyImageView = [[DTLazyImageView alloc] init];
                lazyImageView.delegate = self;
                lazyImageView.tag = i;
                lazyImageView.frame = CGRectMake(x, y,width, height);
                lazyImageView.url = [NSURL URLWithString:@"put image url here"];
                [self.view addSubview:lazyImageView];
                if (! lazyImageView.image) {
                    [lazyImageView startLoading];
                }
    }

}

#pragma mark -
#pragma mark DTLazyImageView Delegate Method

- (void)imageDidLoad: (DTLazyImageView *) lazyImageView
{
    // place your custom code here
}

@end


Note : Using above steps you can easily, implement LazyLoad of images to your application.


Feel free to ask, if any Query.


Thanks & Regards,
Nilesh Prajapati

No comments:

Post a Comment