- [cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"loading.jpg"]];
in cellForRowAtIndexPath:
现在的问题是它只加载可见单元格中的图像,而不是为了我不得不向上和向下滚动以使其加载的屏幕外的单元格.有没有办法我可以加载所有的图像,而不必滚动表视图.
提前致谢!!
解决方法
- - (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;
- }
这里有很多,但实际上,SDWebImage及其SDWebImagePrefetcher正在大力提升.
为了完整起见,我将原来的答案包括在内.
原来的答案:
如果要使用SDWebImage进行某些预取,则可以执行以下操作:
- - (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;
- }
我必须承认,我不喜欢在这里调用我的预取程序(我希望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执行此操作,我在TableModelRow类(支持表的各行的模型类)中保留一个弱指针到web图像操作:
- @property (nonatomic,weak) id<SDWebImageOperation> webImageOperation;
如果还没有,请下载downloadImageIfNeeded例程(您可以看到为什么这个弱点非常重要)我正在检查这个行是否已经有一个操作挂起,然后再启动另一个).我没有对下载的图像做任何事情(简而言之,为了调试目的,记录下载完成的事实),而只是下载并让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,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类(见the documentation).类的名称是非常有希望的,但是看代码,所有的尊重,否则优秀的图书馆,这对我来说并不觉得非常强大(例如,这是一个简单的URL提取列表,如果你再次这样做,它取消了先前的列表,没有“添加到队列”或任何类似的概念.这是一个有希望的概念,但执行有点薄弱.当我尝试它,我的UX受到明显的影响.
所以,我倾向于不使用SDWebImagePrefetcher(至少要改进),并且坚持我的基本预取技术.这不是非常复杂的,但它似乎工作.