UICollectionView

UICollectionView

文章目录

前言

在 iOS 开发中,UITableView 是最常用的列表控件,但当我们需要展示更复杂的网格、瀑布流、轮播等布局时,UICollectionView 便展现出其强大的灵活性。

它比 UITableView 更灵活,可以实现九宫格,瀑布流,图片墙,横向滚动,Banner,商品列表,音乐/视频推荐页

现在大多数 App 首页基本都离不开 UICollectionView,我们仿写应用时也要使用

组成

主要由下面几个部分组成

组件 作用
UICollectionView 主视图
UICollectionViewCell 每个单元格
DataSource 数据源
Delegate 代理
FlowLayout 布局对象

UICollectionView

UICollectionViewFlowLayout

UICollectionViewCell

创建

  1. 创建布局对象
objc 复制代码
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];

设置布局

objc 复制代码
layout.itemSize = CGSizeMake(100, 100);

layout.minimumLineSpacing = 10;

layout.minimumInteritemSpacing = 10;

layout.scrollDirection = UICollectionViewScrollDirectionVertical;
属性 作用
itemSize cell大小
minimumLineSpacing 行间距
minimumInteritemSpacing 列间距
scrollDirection 滚动方向
  1. 创建CollectionView
objc 复制代码
UICollectionView *collectionView =
[[UICollectionView alloc] initWithFrame:self.view.bounds
                   collectionViewLayout:layout];
objc 复制代码
[self.view addSubview:collectionView];
  1. 注册cell

UITableView一样,都需要注册cell

objc 复制代码
[collectionView registerClass:[UICollectionViewCell class]
   forCellWithReuseIdentifier:@"cell"];
  1. 遵守协议

    @interface ViewController ()
    <
    UICollectionViewDataSource,
    UICollectionViewDelegate

objc 复制代码
collectionView.delegate = self;
collectionView.dataSource = self;//设置代理
自定义cell

当然,我们通常不会使用系统cell,根据需要,我们会自定义cell

objc 复制代码
@interface MusicCell : UICollectionViewCell

@property(nonatomic, strong) UIImageView *imageView;

@property(nonatomic, strong) UILabel *titleLabel;

@end

我们需要一个可以展示文字和图片的cell

objc 复制代码
- (instancetype)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];

    if (self) {

        self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];

        [self.contentView addSubview:self.imageView];

        self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, 100, 20)];

        self.titleLabel.textAlignment = NSTextAlignmentCenter;

        [self.contentView addSubview:self.titleLabel];
    }

    return self;
}

因为不需要系统cell,我们需要重写初始化,才能添加自定义的布局,比如imageView,titleLabel

其余功能
  1. 复用机制

屏幕外的Cell会被回收

新的Cell优先复用旧Cell

减少内存消耗

  1. 点击cell触发事件
objc 复制代码
- (void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    NSLog(@"点击了 %ld", indexPath.item);//实现自己的方法
}
  1. 横向滚动
objc 复制代码
layout.scrollDirection =
UICollectionViewScrollDirectionHorizontal;
瀑布流

瀑布流是一种非常经典的布局方式,最大的特点就是每个cell高度不同但是排列紧凑

特点

多列容器:将展示区域划分为若干等宽的列。

逐行填充:元素(图片、卡片等)按顺序依次放置。

最短列优先:每个元素不是按行对齐,而是找当前所有列中累积高度最小的那一列,然后放在该列的底部。

动态布局:由于元素高度可能不同,最终每列的总高度可能不同,整体呈现"左侧右侧参差"的瀑布流效果。

重写方法

系统的Flow Layout默认行是优先级,从左到右摆放cell,放满后再换行,y值必须一样,所以做不了瀑布流

我们需要的瀑布流需要优先列,找到最短列,把cell放进去

  1. 实现prepareLayout
objc 复制代码
- (void)prepareLayout {

    [super prepareLayout];
    [self.attrsArray removeAllObjects];
    [self.columnHeights removeAllObjects];
    NSInteger columnCount = 2;
    for (NSInteger i = 0; i < columnCount; i++) {
        [self.columnHeights addObject:@(0)];
    }
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    CGFloat spacing = 10;
    CGFloat cellWidth = (self.collectionView.frame.size.width - spacing * 3) / 2;
    for (NSInteger i = 0; i < count; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        NSInteger minColumn = 0;
        CGFloat minHeight =
        [self.columnHeights[0] floatValue];
        for (NSInteger j = 1;
             j < self.columnHeights.count; j++) {
            CGFloat currentHeight = [self.columnHeights[j] floatValue];
            if (currentHeight < minHeight) {
                minHeight = currentHeight;
                minColumn = j;
            }
        }
        CGFloat x =  spacing + minColumn * (cellWidth + spacing);
        CGFloat y = minHeight + spacing;
        CGFloat cellHeight = arc4random_uniform(150) + 100;
        CGRect frame = CGRectMake(x,y,cellWidth,cellHeight);
        UICollectionViewLayoutAttributes *attrs =
        [UICollectionViewLayoutAttributes
         layoutAttributesForCellWithIndexPath:indexPath];
        attrs.frame = frame;
        [self.attrsArray addObject:attrs];
        self.columnHeights[minColumn] = @(CGRectGetMaxY(frame));
    }
}

prepareLayout是在CollectionView之前自动调用,计算好所有cell的位置

清空旧数据

因为prepare可能调用很多次,为了避免重复布局,需要使用removeAllObjects清空旧数据

初始化列数列高

NSInteger columnCount = 2;数字是几,列数就是几

再把列高设成0

遍历cell创建indexPath,表示当前是第几个cell

之后我们要找到最短列,先遍历所有列比较高度,确定位置

计算新cell的x和y,x为一个间隔加一个cell的宽度,y就是最短列加一个间隔

然后我们就要使用随机高度,arc4random_uniform系统随机函数生成随机高度

创建attributes,它记录了cell的布局,也就是关于cell的各种属性

属性 作用
frame 位置大小
center 中心点
size 大小
alpha 透明度
transform 变换
zIndex 层级

那我们只使用frame一个属性,也不能直接使用frame,因为CollectionView只认attributes,使用它进行包装系统才能正常显示

  1. 实现collectionViewContentSize
objc 复制代码
- (CGSize)collectionViewContentSize {

    CGFloat maxHeight = 0;
    for (NSNumber *height
         in self.columnHeights) {
        if (height.floatValue > maxHeight) {
            maxHeight = height.floatValue;
        }
    }
    return CGSizeMake(0, maxHeight + 10);
}

这个方法是得到整体高度,决定整个内容能滚多远,创建cell的时候我们要找最短列,在这个方法我们要找最高列

  1. 返回布局属性
objc 复制代码
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

    return self.attrsArray;
}
对比项 UITableView UICollectionView
布局 单列列表 自由布局
排列方式 按行排列 网格/瀑布流
灵活性 较低 很高
横向滚动 不方便 原生支持
适用场景 设置页、聊天 首页、推荐页
自定义能力 一般 很强
学习难度 简单 较复杂

总结

UICollectionView相较于UITableView功能更强大,在App首页中有大量使用,通过学习我们能有仿写应用的能力

相关推荐
水云桐程序员13 小时前
APP 的架构设计
macos·objective-c·cocoa·软件工程
开开心心loky15 小时前
[OC 底层] (四) 多线程相关内容
macos·ios·objective-c·cocoa
白玉cfc15 小时前
【iOS】底层原理:理解dyld
macos·objective-c·cocoa
我欲扶摇九万里17 小时前
Mac版idea快捷键失效的原因及解决方法
java·macos·intellij-idea
水云桐程序员17 小时前
React Native(RN)跨平台 App 架构
react native·objective-c·软件工程·reactnative
秋雨梧桐叶落莳17 小时前
iOS——UIStackView学习
学习·macos·ios·objective-c·cocoa
liyoro1 天前
用 Codex + 提示词生成一个快速打开 Ghostty 的 macOS 小工具
macos·shell·ai编程
A懿轩A2 天前
Claude Code 2026 最新版下载安装教程详细版 涵盖Windows 和 MacOS 安装 附常见问题解决方案
windows·macos
倔强的石头1062 天前
SenseNova-U1 实战体验:从网页版生成,到 Mac 踩坑,再到 CUDA 服务器跑通本地部署
运维·服务器·macos