阅读 2351

手把手带你,优化一个滚动时流畅的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在滑动的时候也没有明显卡顿的地方。 完美。

文章分类
iOS