Sdwebimage the implementation of picture preloading

Source: Internet
Author: User
Tags prefetch uikit

I sdwebimage the library loads the remote picture into a table view I created a custom cell class.

[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"loading.jpg"]];

The problem now is that it loads the images in the visible cells, not just for me to scroll up and down so that the cells they load are located outside the screen. Is there any way to load all the images without scrolling the table view. Thank you in advance!
Original address: Http://stackoverflow.com/questions/15701556/sdwebimage-does-not-load-remote-images-until-scroll

--------------------------------------------------------------------------------------------------------------- ----------
1. If you want to prefetch a row, you can respond UIScrollViewDelegate to the method to determine when the table scrolling is complete, triggering the prefetch of the row. You can perform SDWebImagePrefetcher (in my original reply I was a little dismissive of the class, but it seems to work relatively well now):

 -(void) viewdidload{[super viewdidload];//The details don ' t really matter here, but the idea was to fetch dat A,//call ' Reloaddata ', and then prefetch the other images nsurl *url = [Nsurl urlwithstring:kurlwithjsondata]; Nsurlrequest *request = [Nsurlrequest Requestwithurl:url]; [Nsurlconnection sendasynchronousrequest:request queue:[nsoperationqueue Mainqueue] completionHandler:^ ( Nsurlresponse *response, NSData *data, Nserror *connectionerror) {if (connectionerror) {NSLog (@ "Sendasynchronousreque   St Error:%@ ", connectionerror);  Return  } self.objects = [nsjsonserialization jsonobjectwithdata:data options:0 Error:nil];  [Self.tableview Reloaddata]; [Self prefetchImagesForTableView:self.tableView]; }];} Some of the basic ' uitableviewdatadelegate ' methods has been omitted because they ' re not really relevant  

The following is simple cellForRowAtIndexPath (not entirely relevant, but just indicates that if SDWebImagePrefetcher codego.net, you don't have to be with the surrounding cellForRowAtIndexPath :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *cellIdentifier = @"Cell"; CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; NSAssert([cell isKindOfClass:[CustomCell class]], @"cell should be CustomCell"); [cell.customImageView setImageWithURL:[self urlForIndexPath:indexPath] placeholderImage:nil]; [cell.customLabel setText:[self textForIndexPath:indexPath]]; return cell;}

At the end of these UIScrollViewDelegate scrolls, the Prefetch method has more rows

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ // if `decelerate` was true for `scrollViewDidEndDragging:willDecelerate:` // this will be called when the deceleration is done [self prefetchImagesForTableView:self.tableView];}- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ // if `decelerate` is true, then we shouldn‘t start prefetching yet, because // `cellForRowAtIndexPath` will be hard at work returning cells for the currently visible // cells. if (!decelerate)  [self prefetchImagesForTableView:self.tableView];}

You need a prefetch program. This gets the NSIndexPath values for the cells on each side of the visible cell, gets their image URL, and then prefetch the data.

/** Prefetch A certain number of images for rows prior to and subsequent to the currently visible cells * * @param tablevi EW the TableView for which we ' re going to prefetch images. */-(void) Prefetchimagesfortableview: (UITableView *) tableview{nsarray *indexpaths = [Self.tableview Indexpathsforvisiblerows]; if ([indexpaths count] = = 0) return; Nsindexpath *minimumindexpath = indexpaths[0]; Nsindexpath *maximumindexpath = [Indexpaths lastobject];  They should is sorted already, but if not, update min and Max accordingly for (Nsindexpath *indexpath in indexpaths) { if (Indexpath.section < Minimumindexpath.section | | (indexpath.section = = Minimumindexpath.section && indexpath.row < Minimumindexpath.row))  Minimumindexpath = Indexpath; if (indexpath.section > Maximumindexpath.section | | (indexpath.section = = maximumindexpath.section && indexpath.row > Maximumindexpath.row)) Maximumindexpath = Indexpath; }//build array of imageurls for cells to prefetch NSMUTablearray *imageurls = [Nsmutablearray array]; Indexpaths = [self Tableview:tableview priorindexpathcount:kprefetchrowcount fromindexpath:minimumindexpath]; For (Nsindexpath *indexpath in indexpaths) [Imageurls addobject:[self Urlforindexpath:indexpath]]; Indexpaths = [self Tableview:tableview nextindexpathcount:kprefetchrowcount fromindexpath:maximumindexpath]; For (Nsindexpath *indexpath in indexpaths) [Imageurls addobject:[self Urlforindexpath:indexpath]]; Now prefetch if ([imageurls count] > 0) {[[Sdwebimageprefetcher sharedimageprefetcher] prefetchurls:imageurls];} }

These are used to get Nsindexpath for the previously visible cells, And the rows that follow the visible cells:

/** Retrieve Nsindexpath for a certain number of rows preceding particular nsindexpath in the table view. * * @param tableView the TableView for which we ' re going to retrieve indexpaths.  * @param count the number of rows to retrieve * @param indexpath the indexpath where we ' re going to start (presumably the First visible indexpath) * * @return an array of indexpaths. */-(Nsarray *) TableView: (UITableView *) TableView Priorindexpathcount: (Nsinteger) Count Fromindexpath: (Nsindexpath *) indexpath{Nsmutablearray *indexpaths = [Nsmutablearray array]; Nsinteger row = Indexpath.row; Nsinteger section = indexpath.section;   for (Nsinteger i = 0; I < count, i++) {if (row = = 0) {if (section = = 0) {return indexpaths;    } else {section--;   row = [TableView numberofrowsinsection:section]-1;  }} else {row--; } [indexpaths Addobject:[nsindexpath indexpathforrow:row insection:section]; } return indexpaths;} /** Retrieve Nsindexpath for a certain number of following ParticulaR Nsindexpath in the table view. * * @param tableView the TableView for which we ' re going to retrieve indexpaths.  * @param count the number of rows to retrieve * @param indexpath the indexpath where we ' re going to start (presumably the Last visible indexpath) * * @return an array of indexpaths. */-(Nsarray *) TableView: (UITableView *) TableView Nextindexpathcount: (Nsinteger) Count Fromindexpath: (Nsindexpath *) indexpath{Nsmutablearray *indexpaths = [Nsmutablearray array]; Nsinteger row = Indexpath.row; Nsinteger section = indexpath.section; Nsinteger rowcountforsection = [TableView numberofrowsinsection:section];  for (Nsinteger i = 0; i < count; i++) {row++;   if (row = = rowcountforsection) {row = 0;   section++;   if (section = = [TableView numberofsections]) {return indexpaths;  } rowcountforsection = [TableView numberofrowsinsection:section]; } [indexpaths Addobject:[nsindexpath indexpathforrow:row insection:section]; } return indexpaths;}

There's a lot out there, but in reality, SDWebImage it SDWebImagePrefetcher 's doing heavy lifting. It contains the reason below my original answer: If you want to do with prefetching SDWebImage , you can do as follows: Add blocks to your setImageWithURL call:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ NSLog(@"%s", __FUNCTION__); static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; TableModelRow *rowData = self.objects[indexPath.row]; cell.textLabel.text = rowData.title; [cell.imageView setImageWithURL:rowData.url     placeholderImage:[UIImage imageNamed:@"placeholder.png"]       completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {        [self prefetchImagesForTableView:tableView];       }]; return cell;}

I have to admit, I really don't like my phone. prefetcher General here (I want iOS to have a good Didfinishtablerefresh but it works even though it calls the routines more than I really want. I just make sure that the below program can make sure it doesn't make extra requests. Anyway I write a program prefetch that looks like, for example, the next ten years of imagery:

Const Nsinteger Kprefetchrowcount = 10;-(void) Prefetchimagesfortableview: (UITableView *) tableview{//Determine the Minimum and maximum visible rows nsarray *indexpathsforvisiblerows = [TableView indexpathsforvisiblerows]; Nsinteger Minimumvisiblerow = [indexpathsforvisiblerows[0] row]; Nsinteger Maximumvisiblerow = [indexpathsforvisiblerows[0] row]; For (Nsindexpath *indexpath in indexpathsforvisiblerows) {if (Indexpath.row < minimumvisiblerow) MinimumVisibleRow =  Indexpath.row; if (Indexpath.row > Maximumvisiblerow) maximumvisiblerow = Indexpath.row; }//Now iterate through our model; ' Self.objects ' is a array of ' Tablemodelrow ' objects, one object//For every row of the table. [Self.objects enumerateobjectsusingblock:^ (Tablemodelrow *obj, Nsuinteger idx, BOOL *stop)  {Nsassert ([obj Iskindofclass:[tablemodelrow class], @ "expected Tablemodelrow object"); If the index is within ' kprefetchrowcount ' rows of We visible rows, let ' s//Fetch the image, if it hasn ' t aLready did so.   if ((IDX < minimumvisiblerow && idx >= (minimumvisiblerow-kprefetchrowcount)) | |  (idx > Maximumvisiblerow && idx <= (Maximumvisiblerow + kprefetchrowcount)))  {//My model object has method for initiating a download if needed [obj downloadimageifneeded]; } }];}

In the download program, you can check to see if the image download has started, and if not, start it. To do this there SDWebImage , I keep the weak pointer on the page image processing my TableModelRow class (that is, the model class that backs up the rows of my table):

@property (nonatomic, weak) id<SDWebImageOperation> webImageOperation;

Then I have downloadImageIfNeeded the program to start downloading if it hasn't yet (you'll understand why it weak 's so important ...). I check to see if this line already has another operation pending before the start). I don't do this with the download image (short, for debugging purposes, to record the fact that the download has been done), and just to download, let anything SDImageWeb keep the track of the cached image, so when cellForRowAtIndexPath the image is later asked to scroll down, it's there, ready and waiting.

 -(void) downloadimageifneeded{if (self.webimageoperation) return; Sdwebimagemanager *imagemanager = [Sdwebimagemanager Sharedmanager];             Self.webimageoperation = [Imagemanager downloadWithURL:self.url options:0 Progress:nil completed:^ (UIImage *image, Nserror *error, Sdimagecachetype CacheType, BOOL finished) {NSLog (@ "%s:downloa              Ded%@ ", __function__, Self.title); I ' m not going to does anything with the image, but ' sdwebimage ' have now cached it for me}];}  

Some think it might be called Imagemanager.imagecache Querydiskcacheforkey takes precedence, and after testing it does not look like a need (and downloadwithurl it does). I want to point out that the Sdimageweb Library has the Sdwebimageprefetcher class (see the class's incredible future, but look at the code, with all respect for an original excellent library, This does not feel very (for example, its URL is a simple list to get, if you do it again, it cancels the concept of the previous list "add to queue" or something like that). This is a promising concept, but the implementation is a bit weak, and when I try, my UX suffers obviously. So, I tend to sdwebimageprefetcher (until it's improved, at least), and stick to my prefetch technology. It's not very complicated, but it seems to work.  
2.  This is an example of what you need to do for your purpose. Delegate for your UITableView:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ YourCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YourCustomTableViewCellReuseIdentifier"]; if (!cell) {  cell = [[[YourCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault        reuseIdentifier:CellIdentifier];   } NSString *imageURL = // ... get image url, typically from array [cell loadImageWithURLString:imageURL forIndexPath:indexPath];  return cell;}

, your custom UITableViewCell h file:

#import <UIKit/UIKit.h>#import "UIImageView+WebCache.h"#import "SDImageCache.h"@interface YourCustomTableViewCell{ NSIndexPath *currentLoadingIndexPath;}- (void)loadImageWithURLString:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath;@end

, your custom UITableViewCell m file:

// ... some other methods- (void)loadImageWithURLString:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath{ currentLoadingIndexPath = indexPath; [self.imageView cancelCurrentImageLoad]; [self.imageView setImage:nil]; NSURL *imageURL = [NSURL URLWithString:urlString]; [self.imageView setImageWithURL:imageURL     placeholderImage:nil       options:SDWebImageRetryFailed       completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {  if (currentLoadingIndexPath != indexPath)  {   return;  }  if (error)  {   ... // handle error  }  else  {   [imageView setImage:image];  } }];}// ... some other methods

currentLoadingIndexPathIf we detect another image of the cell, instead of downloading it as needed to scroll the table view image.
3. I have to solve this exact problem and do not want to pre-fetch the overhead. There must be an extra down hood for things to happen with to prevent the loading of built-in ImageView properties, a new Uiimageview works well. My solution is very clean if you don't use the UITableViewCell subclass mind (or already): Subclass of UITableViewCell. In your sub-category, Hide Self.imageview. Create your own Uiimageview child view and set the image of the view. Here is the modified version of my own code (here is the size and location matching overlay for albums set to the iOS photo app): YourTableCell.h

@interface YourTableCell : UITableViewCell @property (nonatomic, strong) UIImageView *coverPhoto;@end

yourtablecell.m

@implementation YourTableCell@synthesize coverPhoto;- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) {  self.imageView.image = nil;  self.coverPhoto = [[UIImageView alloc] init];  // Any customization, such as initial image, frame bounds, etc. goes here.    [self.contentView addSubview:self.coverPhoto]; } return self;}//...@end

Yourtableviewcontroller.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @"Cell"; YourTableCell *cell = (YourTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //... [cell.coverPhoto setImageWithURL:coverUrl placeholderImage:nil options:SDWebImageCacheMemoryOnly]; //...}

Sdwebimage the implementation of picture preloading

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.