如果您想要預取行,您可以響應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
方法滾動完成
當預取更多的行
你顯然需要實現一個預取例程。這將獲取可見單元格每邊的單元格的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
指向Web圖像操作我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
類(見the documentation)。這個類的名字是非常有前途的,但是看着代碼,所有人都尊重一個優秀的庫,這對我來說並不是很健壯(例如,它是一個簡單的URL提取列表,如果你再次執行,它取消了之前的列表,沒有「添加到隊列」或類似的概念)。這是一個很有希望的概念,但執行力有點弱。當我嘗試它時,我的用戶體驗顯着受損。因此,我傾向於不使用SDWebImagePrefetcher
(至少在改進之前),並堅持我的基本預取技術。這不是非常複雜,但它似乎工作。
你確定這些細胞正在創建這些屏幕之外?你知道UITableView的行爲嗎? – Exploring
細胞正在被重新使用 – user2082760
是的,那麼哪些細胞在屏幕外,那麼將爲那些細胞調用實例方法?這不是SDWebImage的問題。 – Exploring