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

记一次 UICollectionView 卡顿掉帧的问题

简述

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

网上一大堆 UITableViewUICollectionView 滚动视图的优化文章, 也看了不少, 但是这个列表真的是简单到不能再简单了 (一个占满了 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 内容被挤压的一个坑