我SDWebImage库加载远程图片到一个表视图中我创建了一个自定义单元格类。
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"loading.jpg"]];
现在的问题是它加载在可见单元格的图像,而不是只为我要上下滚动,使它们加载的cell都位于屏幕之外。有没有什么办法可以加载所有的图像,而无需滚动表视图。 在此先感谢!
原文地址: -------------------------------------------------------------------------------------------------------------------------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;}
有很多在那里,但在现实中,SDWebImage
其SDWebImagePrefetcher
是做繁重。 里面含有的缘故低于我原来的答复 原来的答案: 如果你想要做的与预取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) idwebImageOperation;
然后我有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
类(请参阅类的的是令人难以置信的前途,但看代码,与所有尊重一个原本优秀的库,这不会觉得很(例如,它的网址是一个简单的列表来获取,如果你再这样做,它取消了之前列表“添加到队列”或类似的东西)的概念。这是一个有前途的概念,但在执行有点弱,而当我试了一下,我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#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]; //...}