iOS lazy-loading of table images

我的未来我决定 提交于 2019-11-28 07:44:12
Wolverine

You can achieve lazy loading in your tableview by following these steps :

In your Class.h, put:

NSMutableDictionary *Dict_name;
BOOL isDragging_msg, isDecliring_msg;

Now In Class.m file, put this code in view did load:

Dict_name = [[NSMutableDictionary alloc] init];

In cellForRowAtIndexPath:

if ([dicImages_msg valueForKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]]) { 
    cell.image_profile.image=[dicImages_msg valueForKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]];
}
else
{
    if (!isDragging_msg && !isDecliring_msg)
    {
        [dicImages_msg setObject:[UIImage imageNamed:@"Placeholder.png"] forKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]];
        [self performSelectorInBackground:@selector(downloadImage_3:) withObject:indexPath];
    }
    else
    {
        cell.image_profile.image=[UIImage imageNamed:@"Placeholder.png"];
    }
}

And for the download image the function is:

-(void)downloadImage_3:(NSIndexPath *)path{
    NSAutoreleasePool *pl = [[NSAutoreleasePool alloc] init];

    NSString *str=[here Your image link for download];

    UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:str]]]; 

    [dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:@"image name or image link same as cell for row"]];

    [tableview performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];

    [pl release];
}

And at last put these methods in your class:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    isDragging_msg = FALSE;     
    [tableview reloadData];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    isDecliring_msg = FALSE;
    [tableview reloadData]; 
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    isDragging_msg = TRUE;
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
    isDecliring_msg = TRUE; 
}

Updated answer:

The standard Apple LazyTableImages suffers from a few flaws:

  1. It won't try to retrieve the images until the scrolling finished. I understand why they chose to do that (probably don't want to retrieve images that might be scrolling away), but this causes the images to appear more slowly than necessary.

  2. It does not constrain the number of concurrent requests to some reasonable number. Yes, NSURLConnection will automatically freeze requests until the maximum number of requests falls to some reasonable number, but in a worst case scenario, you can actually have requests time out, as opposed to simply queueing properly.

  3. Once it starts to download the image, it won't cancel it even if the cell subsequently scrolls off of the screen.

The simple fix is to retire IconDownloader and all of the scrolling/decelerating logic and just use the UIImageView category from either SDWebImage or AFNetworking.

If you're going to fix this yourself, it takes a little work to get all of this right. But here is a rendition of LazyTableImages that uses NSOperationQueue to ensure that we (a) limit concurrent requests; and (b) allows us to cancel requests. I confess I prefer the above UIImageView category implementations, better, but I tried to stay with the structure of Apple's original LazyTableImages, while remedying the flaws I've enumerated above.

Assuming your UITableViewCell has an url property and a UIImage property for the image (plus a property or static value for the queue:

- (void) setThumbnailUrlString:(NSString *)urlString
{
    thumbnailUrlString = urlString;

    NSURL *url = [NSURL URLWithString:urlString];

    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
    if ( queue == nil )
    {
        queue = [[NSOperationQueue alloc] init];
    }
    [NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse * resp, NSData     *data, NSError *error) 
    {
        dispatch_async(dispatch_get_main_queue(),^ 
                       {
                            if ( error == nil && data )
                            {
                                UIImage *urlImage = [[UIImage alloc] initWithData:data];
                                thumbnail.image = urlImage;
                            }
                       });
    }];
}

The issue with your implementation is that your image loading method is probably occurring in the main thread. You need to load the image asynchronously and then update the image in the main thread.

To be correct the block should check to see if the response's url matches the requested url if the cell's image loaded too late before the cell was reused for a different image.

I've done this framework which contains lazy load controller. It's easy to use and works out of the box. https://github.com/cloverstudio/CSUtils Tutorial can be found here: http://www.clover-studio.com/blog/using-csutils-ios-framework-for-lazy-loading-images/

This is quite late answer, but with iOS version changes the approach keeps changing.

In order to lazily load the images, here is the recommended approach:

  1. Download URLs of all images and store them into your container (array / objects etc)
  2. Fire a NSURLSessionTask (post iOS 7 only) which runs async on background queue. If you are below iOS 7, you can use NSURLConnection SendAsynchronousRequest API - it's deprecated in iOS 9 so you better get rid of that soon.
  3. Create your images while on background queue
  4. Come back to main queue, ensure that you got the right UITableViewCell, then update the image

Here - the entire approach is described in my tutorial.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!