手把手带你,优化一个滚动时流畅的TableView
手把手带你,优化一个滚动时流畅的TableView
这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战
我的专栏
意识到我的问题
平时使用手机的时间不算少,每天阅读新闻的时候会感觉到新闻类的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飙升率
糟糕的用户滑动体验
不只是用户,对于开发这来讲,这也是不可以接受的体验。
平时接触并使用的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];
}
当然,首次进入页面,列表数据加载完毕后,我们也要加载一次图片的哦。 好的下面看下优化后的结果:
CPU
占用率比之前最高的时候降低了一半多,app在滑动的时候也没有明显卡顿的地方。 完美。