Handling download of image using SDWebImage while reusing UITableViewCell(在重用 UITableViewCell 时使用 SDWebImage 处理图像下载)
问题描述
我使用第三方库 SDWebImage 为我的 UITableView 单元格下载图像,UIImageView 是在单元格内创建的,并在像这样配置单元格时触发请求.
I have used third party library SDWebImage to download image for my UITableView cells, UIImageView is created within cell and fired request while configuring cell like this.
[imageView setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:@"default.jpg"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
}];
它工作正常,但是当我快速滚动时,大多数图像都没有完全下载(我可以在 charles 中看到),因为图像没有被缓存.即使我的单元格被重复使用,我如何缓存已经发送的请求,这样同一个请求就不会多次发送.
Its working fine, however when I scroll fast, most of the images are not fully downloaded (I can see that in charles) because of that images are not cached. How do I cache the already sent request even though my cell got reused, so that same request won't go multiple times.
请忽略任何错字:)
推荐答案
iOS 10 生效,不再需要我原答案的手动预取代码了.只需设置一个 prefetchDataSource
.例如,在 Swift 3 中:
Effective iOS 10, the manual prefetching code of my original answer is no longer needed. Just set a prefetchDataSource
. For example, in Swift 3:
override func viewDidLoad() {
super.viewDidLoad()
tableView.prefetchDataSource = self
}
然后有一个 prefetchRowsAtIndexPaths
它使用 SDWebImagePrefetcher
来获取行
And then have a prefetchRowsAtIndexPaths
which uses SDWebImagePrefetcher
to fetch the rows
extension ViewController: UITableViewDataSourcePrefetching {
public func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
let urls = indexPaths.map { baseURL.appendingPathComponent(images[$0.row]) }
SDWebImagePrefetcher.shared().prefetchURLs(urls)
}
}
你可以拥有标准的cellForRowAt
:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let url = baseURL.appendingPathComponent(images[indexPath.row])
cell.imageView?.sd_setImage(with: url, placeholderImage: placeholder)
return cell
}
就个人而言,我更喜欢 AlamofireImage.所以 UITableViewDataSourcePrefetching
略有不同
Personally, I prefer AlamofireImage. So the UITableViewDataSourcePrefetching
is slightly different
extension ViewController: UITableViewDataSourcePrefetching {
public func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
let requests = indexPaths.map { URLRequest(url: baseURL.appendingPathComponent(images[$0.row])) }
AlamofireImage.ImageDownloader.default.download(requests)
}
}
显然,cellForRowAt
将使用 af_setImage
:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let url = baseURL.appendingPathComponent(images[indexPath.row])
cell.imageView?.af_setImage(withURL: url, placeholderImage: placeholder)
return cell
}
我在下面的原始答案显示,对于 Objective-C,您如何在 10 之前的 iOS 版本中执行此操作(我们必须自己进行预取计算).
My original answer below, shows, for Objective-C, how you might do it in iOS versions before 10 (where we had to do our own prefetch calculations).
这种取消下载不再可见的单元格的行为正是使异步图像检索在您快速滚动时保持如此响应的原因,即使在互联网连接速度较慢的情况下也是如此.例如,如果您快速向下滚动到 tableview 的第 100 个单元格,您真的不希望该图像检索积压在前面 99 行(不再可见)的图像检索之后.我建议单独保留 UIImageView
类别,但如果您想为可能滚动到的单元格预取图像,请使用 SDWebImagePrefetcher
.
This behavior, of canceling the download of cells that are no longer visible is precisely what keeps the asynchronous image retrieval so responsive when you scroll quickly, even with a slow Internet connection. For example, if you quickly scroll down to the 100th cell of the tableview, you really don't want to have that image retrieval to get backlogged behind the image retrieval for the preceding 99 rows (which are no longer visible). I'd suggest leaving the UIImageView
category alone, but instead use SDWebImagePrefetcher
if you want to prefetch images for cells that you're likely to scroll to.
例如,当我调用 reloadData
时,我还预取了当前可见单元格之前和之后的十个单元格的图像:
For example, where I call reloadData
, I also prefetch the images for the ten cells immediately preceding and following the currently visible cells:
[self.tableView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
[self prefetchImagesForTableView:self.tableView];
});
同样,每当我停止滚动时,我都会这样做:
Likewise, anytime I stop scrolling, I do the same:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self prefetchImagesForTableView:self.tableView];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate)
[self prefetchImagesForTableView:self.tableView];
}
就我如何预取前后十个单元格而言,我是这样做的:
In terms of how I do that prefetch of the ten preceding and following cells, I do it like so:
#pragma mark - Prefetch cells
static NSInteger const kPrefetchRowCount = 10;
/** 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 ([minimumIndexPath compare:indexPath] == NSOrderedDescending)
minimumIndexPath = indexPath;
if ([maximumIndexPath compare:indexPath] == NSOrderedAscending)
maximumIndexPath = indexPath;
}
// build array of imageURLs for cells to prefetch
NSMutableArray<NSIndexPath *> *prefetchIndexPaths = [NSMutableArray array];
NSArray<NSIndexPath *> *precedingRows = [self tableView:tableView indexPathsForPrecedingRows:kPrefetchRowCount fromIndexPath:minimumIndexPath];
[prefetchIndexPaths addObjectsFromArray:precedingRows];
NSArray<NSIndexPath *> *followingRows = [self tableView:tableView indexPathsForFollowingRows:kPrefetchRowCount fromIndexPath:maximumIndexPath];
[prefetchIndexPaths addObjectsFromArray:followingRows];
// build array of imageURLs for cells to prefetch (how you get the image URLs will vary based upon your implementation)
NSMutableArray<NSURL *> *urls = [NSMutableArray array];
for (NSIndexPath *indexPath in prefetchIndexPaths) {
NSURL *url = self.objects[indexPath.row].imageURL;
if (url) {
[urls addObject:url];
}
}
// now prefetch
if ([urls count] > 0) {
[[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:urls];
}
}
/** 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<NSIndexPath *> *)tableView:(UITableView *)tableView indexPathsForPrecedingRows:(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<NSIndexPath *> *)tableView:(UITableView *)tableView indexPathsForFollowingRows:(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;
}
这篇关于在重用 UITableViewCell 时使用 SDWebImage 处理图像下载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:在重用 UITableViewCell 时使用 SDWebImage 处理图像下载
基础教程推荐
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01