Начиная с iOS 10 код ручной предварительной выборки моего исходного ответа больше не нужен. Просто установите prefetchDataSource
. Например, в Swift 3:
override func viewDidLoad() {
super.viewDidLoad()
tableView.prefetchDataSource = self
}
И затем есть prefetchRowsAtIndexPaths
, который использует SDWebImagePrefetcher
для выборки строк
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
немного отличается
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, как вы можете сделать это в версиях iOS до 10 (где нам пришлось выполнять собственные расчеты предварительной выборки).
Такое поведение, отмена загрузки ячеек, которые больше не видны, — это именно то, что делает асинхронное извлечение изображений таким отзывчивым при быстрой прокрутке даже при медленном интернет-соединении. Например, если вы быстро прокрутите вниз до 100-й ячейки табличного представления, вы действительно не хотите, чтобы поиск этого изображения отставал от поиска изображения для предыдущих 99 строк (которые больше не видны). Я бы предложил оставить категорию UIImageView
в покое, но вместо этого использовать SDWebImagePrefetcher
, если вы хотите предварительно выбрать изображения для ячеек, до которых вы, вероятно, будете прокручивать.
Например, когда я вызываю reloadData
, я также предварительно выбираю изображения для десяти ячеек, непосредственно предшествующих и следующих за видимыми в данный момент ячейками:
[self.tableView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
[self prefetchImagesForTableView:self.tableView];
});
Точно так же каждый раз, когда я прекращаю прокрутку, я делаю то же самое:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self prefetchImagesForTableView:self.tableView];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate)
[self prefetchImagesForTableView:self.tableView];
}
С точки зрения того, как я делаю эту предварительную выборку десяти предыдущих и следующих ячеек, я делаю это так:
#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;
}
person
Rob
schedule
23.05.2014