UITableView、UICollectionView等按需加载图片(SDWebImage加载)

我们平常会涉及到很多展示图片的,SDWebImage是我们常用的图片加载框架,它非常简单,基本一行代码就能显示图片

[imageView sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder]

但是在网络不好的情况下,图片下载的速度完全跟不上我们滑动的速度,于是有了按需加载

按需加载有两种方式:

第一种为:视图出现时就会下载图片,若是此图片还未下载成功将此单元格滑出屏幕,会取消滑出图片的下载转而下载此时出现的单元格的图片。

第二种为:视图首次出现、停止拖拽、停止减速时就会下载图片,若是此图片还未下载成功将此单元格滑出屏幕,会取消滑出图片的下载转而下载此时出现的单元格的图片。

由于tableView和collectionView的单元格重用机制,很容易造成图片未下载完显示错乱,所以需要在自定义cell中重写prepareForReuse方法,下面的imageVi为自定义UIImageView控件

vc.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    HKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kTableViewCellId forIndexPath:indexPath];
    cell.placeholderImage = self.placeholderImage;
    PersonModel *model = self.dataArr[indexPath.row];
    if (self.segmentControl.selectedSegmentIndex == 0) {//第一种
        cell.tableView = tableView;
        cell.indexPath = indexPath;
        cell.model = model;
    } else {//第二种
        cell.titleLabel.text = model.name;
        cell.imageVi.image = self.placeholderImage;
        UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:model.picture];
        if (cacheImage) {
            NSLog(@"图片尺寸%@",NSStringFromCGSize(cacheImage.size));
            cell.imageVi.image = cacheImage;
        } else {
            if (!tableView.isDragging && !tableView.isDecelerating) {
                [cell.imageVi sd_setImageWithURL:[NSURL URLWithString:model.picture] placeholderImage:self.placeholderImage completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
                    image = [image drawImageWithSize:CGSizeMake(kSCREEN_WIDTH, 600)];
                    cell.imageVi.image = image;
                    [[SDImageCache sharedImageCache] storeImage:image forKey:model.picture completion:nil];
                }];
            }
        }
    }
    return cell;
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        if (self.segmentControl.selectedSegmentIndex == 1) {//适用于第二种
            [self loadImageForCellRows];
        }
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    if (self.segmentControl.selectedSegmentIndex == 1) {//适用于第二种
        [self loadImageForCellRows];
    }
}

#pragma mark - 加载图片
- (void)loadImageForCellRows {
    NSArray *cells = [self.tableView indexPathsForVisibleRows];
    for (NSIndexPath * indexPath in cells) {
        HKTableViewCell *cell = (HKTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
        UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:self.dataArr[indexPath.row]];
        if (cacheImage) {
            cell.imageVi.image = cacheImage;
        } else {
            [cell.imageVi sd_setImageWithURL:[NSURL URLWithString:self.dataArr[indexPath.row]] placeholderImage:[UIImage imageNamed:@"placeholder"]];
        }
    }
}

cell.m

- (void)prepareForReuse {
    [super prepareForReuse];
    self.imageVi.image = self.placeholderImage;//占位图需要,不然网络很差时可看到显示错乱
    [self.imageVi sd_cancelCurrentImageLoad];//此方法作用是取消imageView以前关联的url的下载
}

这儿模拟常规加载数据

- (void)setModel:(PersonModel *)model {
    _model = model;
    if (self.tableView && self.indexPath) {
        self.titleLabel.text = model.name;
        UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:model.picture];
        if (cacheImage) {
            self.imageVi.image = cacheImage;
            NSLog(@"图片尺寸%@",NSStringFromCGSize(cacheImage.size));
        } else {
            NSArray *cells = [self.tableView indexPathsForVisibleRows];
            if ([cells containsObject:self.indexPath]) {
                [self.imageVi sd_setImageWithURL:[NSURL URLWithString:model.picture] placeholderImage:self.placeholderImage completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
                    image = [image drawImageWithSize:CGSizeMake(kSCREEN_WIDTH, 600)];
                    self.imageVi.image = image;
                    [[SDImageCache sharedImageCache] storeImage:image forKey:model.picture completion:nil];
                }];
            }
        }
    }
}

AppDelegate.m

为了避免加载大图内存暴涨crash,我这儿取消了SDWebImage的内存缓存,只在disk缓存

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //下面两行代码主要是防止加载大分辨率图片时内存暴涨crash
    [SDImageCache sharedImageCache].config.shouldCacheImagesInMemory = NO;//缓存图片不放入内存
    //sd中可点击diskCacheReadingOptions跳转到这个属性,提示设置为NSDataReadingMappedIfSafe可提高性能
    [SDImageCache sharedImageCache].config.diskCacheReadingOptions = NSDataReadingMappedIfSafe;
    
    return YES;
}

有时候图片加载多了会出现内存警告,所以还需要在AppDelegate.m中重写下面的方法,内存过高需要清理

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
    [[SDWebImageManager sharedManager] cancelAll];
    [[SDWebImageManager sharedManager].imageCache clearWithCacheType:SDImageCacheTypeDisk completion:nil];
}

demo当然是不能少的啦,demo中图片链接若是失效可自行装载几个图片url(缺资源找度娘)

demo在这儿