注册
iOS

手把手带你,优化一个滚动时流畅的TableView

474878793d814c37b30c998dc23c7c3c~tplv-k3u1fbpfcp-zoom-crop-mark:1304:1304:1304:734.awebp

手把手带你,优化一个滚动时流畅的TableView

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战


我的专栏

  1. iOS 底层原理探索
  2. iOS 底层原理探索 之 阶段总结

意识到我的问题

平时使用手机的时间不算少,每天阅读新闻的时候会感觉到新闻类的app优化的还是很好的,TableView的Cell滚动的时候不会去加载显示图片内容,当一次滑动结束之后,Cell上的新闻图片便开始逐个的加载显示出来,所以整个滑动的过程是很流畅的。这中体验也是相当nice的。

我最开始的做法

开发中TableView的使用是非常值频繁的,当TableViewCell上需要加载图片的时候,是一件比较头疼的事。因为,用户一边滑动TableView,TableView需要一边从网络获取图片。之前的操作都是放在 cellForRowAtIndexPath 中来处理,这就导致用户在滑动TableView的时候,会特别的卡(尤其是滑动特别快时),而且,手机的CPU使用率也会飙的非常的高。对于用户来说,这显然是一个十分糟糕的体验。

糟糕的图片显示 代码

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

ImageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
cell.index = indexPath;

NSMutableDictionary *info = [self.dataSource objectAtIndex:cell.index.row];

NSString *url = [info objectForKey: @"img" ];
NSData *iData = [NSData dataWithContentsOfURL:[NSURL URLWithString: url ]];
cell.img.image = [UIImage imageWithData:iData];
cell.typeL.text = [NSString stringWithFormat:@"%ld-%ld", cell.index.section, cell.index.row];

return cell;
}

糟糕的手机CPU飙升率

未命名.gif

糟糕的用户滑动体验

未命名1.gif

不只是用户,对于开发这来讲,这也是不可以接受的体验。

平时接触并使用的app也非常的多,发现他们多处理方式就是,当用户滑动列表的时候,不再加载图片,等用户的滑动结束之后,会开始逐一的加载图片。这是非常好的优化思路,减轻了CPU的负担,也不会基本不会让用户感觉到页面滚动时候的卡顿。这也就是最开始我描述的我看新闻app的使用体验。

收到这个思路的启发,我们开始着手将上面糟糕的体验作一下优化吧。

总结思路开启优化之路

那么,带着这个优化思路,我开始了对于这个TableView 的优化。

  • 首先,我们只加载当前用户可以看到的cell上的图片。
  • 其次,我们一次只加载一张图片。

要完成以上两点,图片的加载显示就不能在cellForRowAtIndexPath中完成,我们要定义并实现一个图片的加载显示方法,以便在合适的时机,调用刷新内容显示。

loadSeeImage 加载图片的优化

#pragma mark load Images
- (void)loadSeeImage {

//记录本次加载的几张图片
NSInteger loadC = 0;

// 用户可以看见的cells
NSArray *cells = [self.imageTableView visibleCells];

// 调度组
dispatch_group_t group = dispatch_group_create();

for (int i = 0; i < cells.count; i++) {

ImageTableViewCell *cell = [cells objectAtIndex:i];

NSMutableDictionary *info = [self.dataSource objectAtIndex:cell.index.row];
NSString *url = [info objectForKey: @"img" ];

NSString *data = [info objectForKey:@"data"];

if ([data isKindOfClass:[NSData class]]) {


}else {

// 添加调度则到我们的串行队列中去
dispatch_group_async(group, self.loadQueue, ^{

NSData *iData = [NSData dataWithContentsOfURL:[NSURL URLWithString: url ]];
NSLog(@" load image %ld-%ld ", cell.index.section, cell.index.row);
if (iData) {
// 缓存
[info setValue:@"1" forKey:@"isload"];
[info setValue:iData forKey:@"data"];
}
NSString *isload = [info objectForKey:@"isload"];

if ([isload isEqualToString:@"0"]) {

dispatch_async(dispatch_get_main_queue(), ^{

cell.img.image = [UIImage imageNamed:@""];
}); }else {

if (iData) {

dispatch_async(dispatch_get_main_queue(), ^{
//显示加载后的图片
cell.img.image = [UIImage imageWithData:iData];
});
}
}

});

if (i == cells.count - 1) {

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 全部加载完毕的通知
NSLog(@"load finished");
});
}

loadC += 1;
}
}

NSLog(@"本次加载了 %ld 张图片", loadC);
}

其次就是 loadSeeImage 调用时机的处理,我们要做到用户在滑动列表之后加载,就是在下面两处加载:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView   {  

[self loadSeeImage];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {

if (scrollView.isDragging || scrollView.isDecelerating || scrollView.isTracking) {
return;
}
[self loadSeeImage];
}

当然,首次进入页面,列表数据加载完毕后,我们也要加载一次图片的哦。 好的下面看下优化后的结果:

优化xcode.gif

优化phone.gif

CPU占用率比之前最高的时候降低了一半多,app在滑动的时候也没有明显卡顿的地方。 完美。

0 个评论

要回复文章请先登录注册