SDWebImage不加载远程图像,直到涡(SDWebImage does not load rem

2019-08-18 04:58发布

我使用SDWebImage库远程图像加载到其使用我已经创建的自定义小区类别的表图。 我简单地使用

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

在的cellForRowAtIndexPath:现在的问题是,它加载在可见光图像的细胞,而不是为了那些屏幕之外细胞,我必须上下滚动,使其加载。 有没有什么办法可以加载所有的图像,而无需滚动表视图。 提前致谢!!

Answer 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 ,你不必惹周围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 ,你可以做类似如下:

  1. 完成块添加到您的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委托方法),但它的作品,即使它调用子程序更多的时间比我真正想要的。 我只是确保下面,下面的程序可以确保它不会让多余的请求。

  2. 无论如何,我写的预取程序,看起来,比如说,未来十年的图像:

     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]; } }]; } 
  3. 在下载程序中,你可以检查,看看是否已开始下载图片,如果没有,则启动它。 要使用做到这一点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.imageCache实例方法queryDiskCacheForKey第一,但做了一些测试后,像是一个需要它不看(和downloadWithURL的确,对于我们来说,反正)。

我要指出的是, SDImageWeb库有SDWebImagePrefetcher类(见文档 )。 类的名称是非常有前途的,但看代码,与所有尊重原本很好的书房,这不会觉得很健壮,我(例如,它是URL的一个简单的列表来获取,如果你再这样做,它取消无概念“添加到队列”或类似的东西)的前列表。 这是一个有前途的概念,但在执行弱一点。 当我试了一下,我的UX明显受到影响。

所以,我倾向于不使用SDWebImagePrefetcher (直到它的改进,至少),并坚持自己的基本的预取技术。 这是不是非常复杂,但它似乎工作。



Answer 2:

我必须解决这个确切的问题,并没有想预取的开销。 必须有一些额外的下引擎罩的东西有防止装载内置ImageView的属性发生的事情,因为一个新的UIImageView工作得很好。

我的解决方案是很干净,如果你不使用的UITableViewCell的子类介意(或已经):

  1. 子类的UITableViewCell。
  2. 在你的子类,隐藏self.imageView。
  3. 创建您自己的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];
    //...
}


Answer 3:

这是一个例子,你需要实现这个你的目的。
您的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需要,如果我们再用这个细胞另一张图片,而不是像这是当用户滚动下载表格视图来检测。



Answer 4:

我遇到了同样的问题,我发现的UIImageView +取消的WebCache上次下载时,一个新的下载来的。

我不知道这是否是作者的意图。 所以我写了一个新的category上SDWebImage的UIImageView基地。

使用方便:

[cell.imageView mq_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                   groupIdentifier:@"customGroupID"
                         completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {

                         }];

要查看更多: ImageDownloadGroup

高级应用:

//  create customGroup
MQImageDownloadGroup *customGroup = [[MQImageDownloadGroup alloc] initWithGroupIdentifier:@"tableViewCellGroup"];
customGroup.maxConcurrentDownloads = 99;

//  add to MQImageDownloadGroupManage
[[MQImageDownloadGroupManage shareInstance] addGroup:customGroup];

//  use download group
[cell.imageView mq_setImageWithURL:@"https://xxx"
                   groupIdentifier:@"tableViewCellGroup"
                         completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {

                         }];


文章来源: SDWebImage does not load remote images until scroll