首页 记一次 UICollectionView 卡顿掉帧的问题
文章
取消

记一次 UICollectionView 卡顿掉帧的问题

简述

最近项目有一个 单列表页面 计划要修改成 多列表页面, 虽然目前还是单列表, 但是既然有这样的计划, 就自行先实现了. 一开始 单列表 使用的是 UITableView 全程流畅滚动. 但是换成 UICollectionView 的 单列表 之后, 列表就开始出现了滑动 掉帧 的情况. 所有逻辑都是一模一样的, 仅仅是换了一个列表控件, 这个情况还是蛮奇怪的, 只好查找一下是不是什么被地方忽略了.

网上一大堆 UITableView 和 UICollectionView 滚动视图的优化文章, 也看了不少, 但是这个列表真的是简单到不能再简单了 (一个占满了 Cell 的 UIImageView 而已), 之前写那么多这样的列表也没出现 掉帧 的情况, 只好一一排查.

排查问题

优化方面的问题有简单有复杂, 先从简单的方面做起:

1.是否主线程处理大量数据造成了卡顿?

大批量的数据处理都在 子线程 进行, cell 也只是在 setter 里面进行简单的 值展示 赋值和一个 图片文件读取 以及 UIImage 对象的赋值.

注释掉 文件读取 代码之后卡顿掉帧的情况依旧存在. 注释掉整个 setter 方法之后卡顿依旧.

2.是否有多余的无需实现的代理方法?

因为列表高度是根据内容变化的, 因此代理方法仅仅实现了 UICollectionViewDelegateFlowLayout 的

1
2
3
4
5
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    // 宽度: collectionView 的宽度
    // 高度: 为了适应不同屏幕, 高度 * 宽度比系数
    return CGSizeMake(CGRectGetWidth(collectionView.bounds), height * scale);
}

其他需要计算的代理方法均未实现. 针对此处优化, 就对 每行的高度 做了 缓存, 问题也未得到解决.

3.检查一下 Cell 内部 UI 约束的代码

就这么多, 的确是简单到不能再简单了.

1
2
3
4
5
6
7
8
#pragma mark - UI

- (void)setupUI {
    [self.contentView addSubview:self.imageView];
    [_imageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(UIEdgeInsetsZero);
    }];
}

既然要优化, 此处 Masonry 的约束也不是必须的, 这样写也是因为布局简单, 认为不太会造成太严重的性能上的问题.

把布局的代码改成这样:

1
2
3
- (void)layoutSubviews {
    _scrollView.frame = self.bounds;
}

跑起来看看, 不卡了…

真的不敢相信… 就这么一个 约束 就能造成这么严重的性能问题???

总结

这次的问题从发现到解决花费了将近 3 个小时, 仅仅一个看似没有任何问题的 自动布局 造成的很严重的问题. 自动布局 的 性能问题 对于做过一段时间 iOS 开发的来讲, 都是常识. 但是自认为:

Cell 布局简单, 自动布局的约束造成的性能问题微乎其微.

因此从一开始就没从这方面来考虑这个问题, 导致花了半个下午的时间来解决这么一个看似简单的 BUG. 看似一个简单的 约束 却让整个列表产生了严重的性能问题.

就当是偷懒和安于习惯的惩罚吧, 吸取教训才是最重要的.

本文由作者按照 CC BY 4.0 进行授权

iOS 配置 App 的 Associated Domains

UITabBar 内容被挤压的一个坑