UITablevView的懒加载时列表优化的最重要的方法之一,比如说今日头条就用了懒加载:快速滑动时,不加载图片,滑动将停止时才加载图片。
一、UITablevView的懒加载的原理:
1、当用户拖动tableview时,会加载cell中的图片。
2、当用户快速滑过tableview时不加载图片。
3、在tableview滑动即将结束时加载cell中的图片,这样就节省了下载图片和加载图片时的开销(多线程的开辟,内存的消耗 )。
上面的原理要求我们熟悉tableView的各种状态(拖动、滑动、减速、停止)等。我们知道UITableView继承自UIScrollView,而UIScrollView的各中状态我们时可以从它的代理方法中获得,从而也就获得了tableVew的各种状态,见:拖拽UIScrollView时的delegate调用调用顺序。
简单懒加载的核心代码:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.targetRect =nil;
[selfloadVisibleCells]; |
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inoutCGPoint *)targetContentOffset {
CGRect targetRect = CGRectMake(targetContentOffset->x, targetContentOffset->y, scrollView.frame.size.width, scrollView.frame.size.height);
self.targetRect = [NSValuevalueWithCGRect:targetRect];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
self.targetRect =nil;
[self loadVisibleCells]; |
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
static NSString *cellIdentifier =@"ImageCell";
ImageCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[[NSBundlemainBundle]loadNibNamed:@"ImageCell"owner:selfoptions:nil]firstObject];
}
[selfloadCell:cellwithIndexPath:indexPath];
}
- (void)loadCell:(ImageCell *)cell withIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
CGRect cellFrame = [self.tableView rectForRowAtIndexPath:indexPath]; |
if (self.targetRect && !CGRectIntersectsRect([self.targetRect CGRectValue], cellFrame)) {
//[cell.bigImageView sd_setImageWithUR。。。加载图片
}
}
- (void) loadVisibleCells {
NSArray *cells = [self.tableViewvisibleCells];
for (ImageCell *cellin cells) {
}
}
二、懒加载存在的问题:
1 、我们虽然在减速过程中没有加载图片,但是显示的cell上依然有图片,这是怎么回事?我们知道tableView的cell是重用的,因为我们在滑动停止时才重新加载图片,所以减速过程中还是上次加载的图片。也就是经常说的图片的窜位问题(重用bug)。
2、上面的方案是拖动或滑动即将停止时再加载图片,在减速过程中即使图片缓存过了也不加载,这显然不合理。
三、进一步优化:
上面的两个问题归根到底是由于在滑动减速过程中没有对cell的图片做处理,也就是说我们可以在减速过程中,我们对已缓存过的图片也加载。
这就涉及到通过url判断图片是否已缓存,这个如果是手动缓存的话比较麻烦,但别忘了SDWebImage这个神器,它在图片的一步下载和缓存方面做的是非常出色的,对下载过的图片都会根据url自动缓存!所以通过下面的代码就可以得知图片是否有已缓存
SDWebImageManager *manager = [SDWebImageManagersharedManager];
SDImageCache *cache = [manager imageCache];
NSString *key = [managercacheKeyForURL:targetURL]; //targetURL就是需要加载的图片的URL的NSURL类型
if ([cache imageFromMemoryCacheForKey:key]) {
//加载图片
}
也就是我们在减速的时候每次加载cell时,判断当前的cell中image图片的url和数据模型中的是否一样,不一样的话,就先隐藏cell的图片,然后根据url区加载缓存中的图片,如果加载到了,则显示图片,否则不做处理,这样就解决了窜位的问题。同时也解决了减速不加载已缓存的图片问题!核心代码:
if (![[cell.photoViewsd_imageURL]isEqual:targetURL]) {
cell.photoView.alpha =0.0;
SDWebImageManager *manager = [SDWebImageManagersharedManager];
CGRect cellFrame = [self.tableViewrectForRowAtIndexPath:indexPath];
BOOL shouldLoadImage = YES;
if (self.targetRect && !CGRectIntersectsRect([self.targetRectCGRectValue], cellFrame)) { //判断是否在减速
SDImageCache *cache = [manager imageCache];
NSString *key = [manager cacheKeyForURL:targetURL];
if (![cache imageFromMemoryCacheForKey:key]) {
shouldLoadImage = NO;
}
}
if (shouldLoadImage) {
[cell.bigImageView sd_setImageWithURL:targetURLplaceholderImage:niloptions:SDWebImageHandleCookiescompleted:^(UIImage *image,NSError *error, SDImageCacheType cacheType,NSURL *imageURL) {
if (!error && [imageURL isEqual:targetURL]) {
[UIViewanimateWithDuration:0.25animations:^{
cell.bigImageView.alpha =1.0;
}];
}
}];
}
}