• SDWebImage图片预加载的实现


    我SDWebImage库加载远程图片到一个表视图中我创建了一个自定义单元格类。

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

    现在的问题是它加载在可见单元格的图像,而不是只为我要上下滚动,使它们加载的cell都位于屏幕之外。有没有什么办法可以加载所有的图像,而无需滚动表视图。 在此先感谢!
    原文地址: http://stackoverflow.com/questions/15701556/sdwebimage-does-not-load-remote-images-until-scroll

     

    -------------------------------------------------------------------------------------------------------------------------
    1. 如果你想预取行,你可以回应UIScrollViewDelegate方法,以确定何时台滚动完成时,触发的行的预取。您可以执行SDWebImagePrefetcher(在我原来的答复我是类的一个小不屑一顾,但它似乎工作相对现在好了):

    - (void)viewDidLoad
    {
     [super viewDidLoad];
     // the details don't really matter here, but the idea is to fetch data, 
     // 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(@"sendAsynchronousRequest 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 have been omitted because they're not really relevant
    

    下面是简单的cellForRowAtIndexPath(不完全相关,但只是表明,如果SDWebImagePrefetcher CodeGo.net,你不必与周围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;
    }
    

    这些UIScrollViewDelegate滚动结束时,预取的方法更多的行

    - (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];
    }
    

    你需要一个预取程序。这得到了NSIndexPath对于在可见单元格的每一侧的单元格的值,得到他们的图像URL,然后预取数据。

    /** Prefetch a certain number of images for rows prior to and subsequent to the currently visible cells
     *
     * @param tableView 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 be 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];
     }
    }
    

    这些是用于获取NSIndexPath对于前可见单元格,以及那些跟随可见单元格的行:

    /** 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;
    }
    

    有很多在那里,但在现实中,SDWebImageSDWebImagePrefetcher是做繁重。 里面含有的缘故低于我原来的答复 原来的答案: 如果你想要做的与预取SDWebImage,你可以做如下所示: 添加块到您的setImageWithURL调用:

    - (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;
    }
    

    我必须承认,我真的不喜欢我打电话prefetcher常规这里(我希望iOS的有很好的didFinishTableRefresh但它的作品,即使它调用例程超过我真正想要的。我只是确保低于下面的程序可以确保它不会让多余的请求。 反正我写一个程序预取,看起来,比如说,未来十年的影像:

    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 an 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 our visible rows, let's
      // fetch the image, if it hasn't already done 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];
      }
     }];
    }
    

    在下载程序中,您可以检查,看看图像下载已经开始,如果没有,则启动它。要做到这一点有SDWebImage,我保持weak指针在网页图像处理我的TableModelRow类(即备份我的表的各行的模型类):

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

    然后我有downloadImageIfNeeded程序开始下载,如果它还没有(你就会明白为什么做了weak是如此重要......我检查,看看这行已经有另一个开始之前的操作挂起)。我不这样做与下载图像(短的,用于调试目的,记录的事实,下载已完成),而只是下载,让任何事情SDImageWeb保持缓存图像的轨道,所以当cellForRowAtIndexPath后来要求形象向下滚动,它的存在,准备和等待。

    - (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: downloaded %@", __FUNCTION__, self.title);
                  // I'm not going to do anything with the image, but `SDWebImage` has now cached it for me
                 }];
    }
    

    部分认为它可能更叫imageManager.imageCachequeryDiskCacheForKey优先,做测试后,它看起来并不像是一个需要(和downloadWithURL确实是这样)。 我要指出的是,SDImageWeb库有SDWebImagePrefetcher类(请参阅类的的是令人难以置信的前途,但看代码,与所有尊重一个原本优秀的库,这不会觉得很(例如,它的网址是一个简单的列表来获取,如果你再这样做,它取消了之前列表“添加到队列”或类似的东西)的概念。这是一个有前途的概念,但在执行有点弱,而当我试了一下,我UX遭受明显。 所以,我倾向于SDWebImagePrefetcher(直到它的改善,至少),并坚持我的预取技术。它并不十分复杂,但它似乎工作。 
    2. 这是一个例子,你需要这样你的目的。 您的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;
    }
    

    ,您的自定义的UITableViewCell h文件:

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

    ,您的自定义的UITableViewCell m文件:

    // ... 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
    

    currentLoadingIndexPath如果我们检测到该小区的另一幅图像,而不是将其下载滚动表视图图像所需。 
    3. 我必须解决这个确切的问题,不想预取的开销。必须有额外的下引擎罩的事情发生与防止装载内置ImageView的属性,一个新的UIImageView工作得很好。 我的解决方案是非常干净的,如果你不使用的UITableViewCell的子类介意(或已经): 子类的UITableViewCell。 在你的子类,隐藏self.imageView。 创建您自己的UIImageView子视图,并设置该视图的形象。 下面是我自己的代码(这里是设置到了iOS照片应用程序的相册的大小和位置匹配覆盖)的修改版本: 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];
     //...
    }
    我的能力超乎我的想象 我的奋斗决定我的未来
  • 相关阅读:
    函数调用本质
    互联网协议入门
    iOS开发系列-Block本质篇
    iOS组件化开发-CocoaPods简介
    版本控制-Git
    iOS开发系列-NSDate
    iOS开发系列-线程同步技术
    Python 抓取网页gb2312乱码问题
    常用正则表达式
    Java 命名规范
  • 原文地址:https://www.cnblogs.com/zhaoyanpeng/p/4829667.html
Copyright © 2020-2023  润新知