iOS开发中UICollectionView的用法详解第一篇(UICollectionViewLayout)

        UICollectionView 是 iOS 开发中非常灵活且强大的 UI 组件,广泛用于展示网格状布局或其他自定义布局的数据。与 UITableView 类似,UICollectionView 能展示多行多列的内容,并且可以高度自定义每个单元格的展示样式和布局。本文将详细介绍 UICollectionView 的基本用法,包括初始化、数据源设置、布局以及高级用法。

1. UICollectionView的简单用法

1.storyboard用法

         使用Storyboard创建UICollectionView的时候,我们需要以下几个步骤:

1.storyboard中添加UICollectionView

1.打开你项目中的 Main.storyboard,或者根据需要新建一个 Storyboard 文件

2.拖拽一个UICollectionView到Storyboard中并且设置好约束

如下图1所示:

图1.storyboard

2.创建并关联自定义 UICollectionViewCell

1.添加自定义单元格:我这里使用的系统的UICollectionViewCell

2.设置Cell的Identifier:选择刚创建的 UICollectionViewCell,在 Attributes Inspector 中,设置一个 Identifier(如 MyCell),这个标识符稍后会用于代码中复用单元格。

3.配置ViewController中的UICollectionView

1.设置ViewController为UICollectionView的数据源和代理

@interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>

@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;

@end

2. 在 Storyboard 中关联 UICollectionView:

        打开 Storyboard,选择 UICollectionView,按住 control 键并拖动到 ViewController 文件中的 @property,完成 IBOutlet 连接。

        如下所示:

        图2.ViewController关联UICollectionView

        然后实现数据源和代理方法即可。

        完整代码如下:

#import "ViewController.h"

@interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>

@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.collectionView.dataSource = self;
    self.collectionView.delegate = self;
}
#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 20;  // 返回 item 数量
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
    cell.contentView.backgroundColor = [self randomColor];
    // 配置 cell,比如设置 label 文本
    return cell;
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Selected item at index %ld", (long)indexPath.item);
}

- (UIColor *)randomColor {
    CGFloat red = (CGFloat)arc4random_uniform(256) / 255.0;
    CGFloat green = (CGFloat)arc4random_uniform(256) / 255.0;
    CGFloat blue = (CGFloat)arc4random_uniform(256) / 255.0;
    
    return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}

@end

        运行代码,效果如下:

图3.简单的UICollectionView用法

2.纯代码布局

        当然上述的功能,我们也可以使用纯代码实现:

#import "ViewController.h"

@interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>

@property (strong, nonatomic) UICollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 设置 UICollectionView 的布局
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    
    // 初始化 UICollectionView
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    self.collectionView.backgroundColor = [UIColor whiteColor];  // 设置背景色
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    // 注册 UICollectionViewCell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"MyCell"];
    
    // 将 UICollectionView 添加到视图中
    [self.view addSubview:self.collectionView];
}
#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 20;  // 返回 item 数量
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
    cell.contentView.backgroundColor = [self randomColor];
    // 配置 cell,比如设置 label 文本
    return cell;
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Selected item at index %ld", (long)indexPath.item);
}

- (UIColor *)randomColor {
    CGFloat red = (CGFloat)arc4random_uniform(256) / 255.0;
    CGFloat green = (CGFloat)arc4random_uniform(256) / 255.0;
    CGFloat blue = (CGFloat)arc4random_uniform(256) / 255.0;
    
    return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}

@end

2.UICollectionViewLayout

        通过上面的例子我们发现当我们初始化UICollectionView的时候,必须设置一个UICollectionViewLayout。

        默认情况下,如果我们设置UICollectionViewLayout,系统会为UICollectionView设置一个UICollectionViewFlowLayout。我们先看看UICollectionViewFlowLayout的属性和方法。

1.UICollectionViewFlowLayout

1.UICollectionViewFlowLayout的属性

1.itemSize

        这个属性用于设置每个单元格 (item) 的大小。

layout.itemSize = CGSizeMake(100, 100);  // 设置每个 item 的大小为 100x100

2.minimumLineSpacing

        这个属性用于控制行与行之间的最小间距(垂直滚动方向时,指每行之间的距离;水平滚动方向时,指每列之间的距离)。

layout.minimumLineSpacing = 20;  // 设置行间距为 20

3.minimumInteritemSpacing

        控制每一行或每一列中单元格之间的最小间距(垂直滚动时,指同一行内 item 之间的距离;水平滚动时,指同一列内 item 之间的距离)。

layout.minimumInteritemSpacing = 15;  // 设置 item 之间的间距为 15

4.sectionInset

        控制每个 section 的四边间距,即距离 UICollectionView 边缘的距离(上、左、下、右)。

layout.sectionInset = UIEdgeInsetsMake(10, 20, 10, 20);  // 上、左、下、右分别为 10, 20, 10, 20

5.scrollDirection

        这个属性用于控制 UICollectionView 的滚动方向。

        默认为垂直滚动。

layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;  // 设置为水平滚动

6.headerReferenceSize

        这个属性用于设置 section 头视图的宽度和高度。        

layout.headerReferenceSize = CGSizeMake(self.view.frame.size.width, 50);  // 头视图宽度与屏幕等宽,高度为 50

7.footerReferenceSize

        这个属性用于设置 section 尾视图的宽度和高度。

layout.footerReferenceSize = CGSizeMake(self.view.frame.size.width, 30);  // 尾视图宽度与屏幕等宽,高度为 30

8.estimatedItemSize

        这个属性用于提供一个估计的 item 大小,实际大小会根据内容进行自动调整。适用于需要自适应高度的 cell。

        当 cell 的大小不固定时,使用该属性可以启用自动布局。

layout.estimatedItemSize = CGSizeMake(100, 100);  // 设置估计的 item 大小,系统将根据内容调整实际大小

9.sectionHeadersPinToVisibleBounds

        这个属性用于控制 section 头视图是否固定在视图顶部(类似于 UITableView 的 section header 固定效果)。

        设置为 YES,头视图在滚动时将固定在顶部。

layout.sectionHeadersPinToVisibleBounds = YES;  // 设置头视图固定在顶部

10.sectionFootersPinToVisibleBounds

        这个属性用于控制 section 尾视图是否固定在视图底部。

        设置为 YES,尾视图在滚动时将固定在底部。

layout.sectionFootersPinToVisibleBounds = YES;  // 设置尾视图固定在底部

2.简单的例子

        理解了UICollectionViewFlow的一些属性,我们可以使用UICollectionViewFlowLayout写出一些漂亮的UI效果。

1.九宫格布局

        我们设置下UICollectionView的itemSize便可以轻松的实现九宫格布局。

       代码如下:

#import "ViewController.h"

@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource>

@property (strong, nonatomic) UICollectionView *collectionView;
@property (strong, nonatomic) NSArray *imagesArray;  // 保存图片的数组

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"123";
    // 初始化图片数组
    self.imagesArray = @[
        [UIImage imageNamed:@"random1"],
        [UIImage imageNamed:@"random2"],
        [UIImage imageNamed:@"random3"],
        [UIImage imageNamed:@"random4"],
        [UIImage imageNamed:@"random5"],
        [UIImage imageNamed:@"random6"],
        [UIImage imageNamed:@"random7"],
        [UIImage imageNamed:@"random8"],
        [UIImage imageNamed:@"random9"]
    ];
    
    // 设置 UICollectionView 的布局
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.minimumLineSpacing = 0;
    layout.minimumInteritemSpacing = 0;
    layout.itemSize = CGSizeMake(self.view.bounds.size.width / 3.0, self.view.bounds.size.width / 3.0);
    
    // 初始化 UICollectionView
    self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
    self.collectionView.backgroundColor = [UIColor whiteColor];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    // 注册 UICollectionViewCell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"MyCell"];
    
    // 将 UICollectionView 添加到视图中
    [self.view addSubview:self.collectionView];
    
    // 设置 Auto Layout 约束
    self.collectionView.translatesAutoresizingMaskIntoConstraints = NO;
    [NSLayoutConstraint activateConstraints:@[
        [self.collectionView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], // 顶部安全距离以下
        [self.collectionView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],             // 左边距为 0
        [self.collectionView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],           // 右边距为 0
        [self.collectionView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]                // 底部边距为 0
    ]];
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.imagesArray.count;  // 返回图片数组的数量
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
    
    // 移除旧的 UIImageView 防止重用问题
    for (UIView *subview in cell.contentView.subviews) {
        [subview removeFromSuperview];
    }
    
    // 创建 UIImageView 并设置图片
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds];
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    imageView.image = self.imagesArray[indexPath.item];  // 设置图片
    
    // 将 UIImageView 添加到 cell 的 contentView 中
    [cell.contentView addSubview:imageView];
    
    return cell;
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Selected item at index %ld", (long)indexPath.item);
}

@end

        运行之后,效果如下:

图4.九宫格布局

2.瀑布流效果

        要使用 UICollectionViewFlowLayout 实现瀑布流效果,实际上可以通过一些简单的调整来实现多列的布局,但 UICollectionViewFlowLayout 本身并不支持瀑布流(即动态高度的多列布局)。不过,你可以自定义 UICollectionViewFlowLayout 或者使用开源的框架,比如 UICollectionViewWaterfallLayout。

        如果你想通过 UICollectionViewFlowLayout 实现一个类似的效果,布局每个 cell 的宽度是固定的,而高度根据内容来动态调整,这里提供一个方法来实现两列的瀑布流效果:

1. 自定义 UICollectionViewFlowLayout:

        通过继承 UICollectionViewFlowLayout,我们可以动态调整 cell 的高度,并保持列间距一致。

        WaterfallFlowLayout.h代码如下:

#import <UIKit/UIKit.h>

@interface WaterfallFlowLayout : UICollectionViewFlowLayout

@property (nonatomic, assign) NSInteger numberOfColumns; // 列数
@property (nonatomic, assign) CGFloat itemSpacing;       // item 间距

@end

        WaterfallFlowLayout.m代码如下:

#import "WaterfallFlowLayout.h"

@interface WaterfallFlowLayout()

@property (nonatomic, strong) NSMutableArray *attributesArray; // 存储布局属性
@property (nonatomic, strong) NSMutableArray *columnHeights;   // 每列的高度

@end

@implementation WaterfallFlowLayout

- (void)prepareLayout {
    [super prepareLayout];
    
    // 初始化数据
    self.attributesArray = [NSMutableArray array];
    self.columnHeights = [NSMutableArray array];
    
    for (NSInteger i = 0; i < self.numberOfColumns; i++) {
        [self.columnHeights addObject:@(self.sectionInset.top)];
    }
    
    // 计算每一个 item 的位置
    CGFloat totalWidth = self.collectionView.bounds.size.width;
    CGFloat availableWidth = totalWidth - self.sectionInset.left - self.sectionInset.right - (self.itemSpacing * (self.numberOfColumns - 1));
    CGFloat itemWidth = availableWidth / self.numberOfColumns;
    
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    
    for (NSInteger i = 0; i < itemCount; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        // 找到高度最短的那一列
        NSInteger shortestColumnIndex = [self shortestColumnIndex];
        CGFloat xOffset = self.sectionInset.left + (itemWidth + self.itemSpacing) * shortestColumnIndex;
        CGFloat yOffset = [self.columnHeights[shortestColumnIndex] floatValue];
        
        // 动态计算每个 item 的高度(可以基于内容或者随机高度)
        CGFloat itemHeight = 100 + arc4random_uniform(100); // 示例中随机高度
        
        attributes.frame = CGRectMake(xOffset, yOffset, itemWidth, itemHeight);
        [self.attributesArray addObject:attributes];
        
        // 更新该列的高度
        self.columnHeights[shortestColumnIndex] = @(yOffset + itemHeight + self.minimumLineSpacing);
    }
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *visibleAttributes = [NSMutableArray array];
    
    for (UICollectionViewLayoutAttributes *attributes in self.attributesArray) {
        if (CGRectIntersectsRect(attributes.frame, rect)) {
            [visibleAttributes addObject:attributes];
        }
    }
    
    return visibleAttributes;
}

- (CGSize)collectionViewContentSize {
    CGFloat contentHeight = [[self.columnHeights valueForKeyPath:@"@max.self"] floatValue];
    return CGSizeMake(self.collectionView.bounds.size.width, contentHeight + self.sectionInset.bottom);
}

// 找到高度最短的列
- (NSInteger)shortestColumnIndex {
    NSInteger shortestIndex = 0;
    CGFloat shortestHeight = [self.columnHeights[0] floatValue];
    
    for (NSInteger i = 1; i < self.columnHeights.count; i++) {
        CGFloat currentHeight = [self.columnHeights[i] floatValue];
        if (currentHeight < shortestHeight) {
            shortestHeight = currentHeight;
            shortestIndex = i;
        }
    }
    
    return shortestIndex;
}

@end

2. 在 ViewController 中使用这个自定义布局

#import "ViewController.h"
#import "WaterfallFlowLayout.h"

@interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) UICollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 创建自定义的瀑布流布局
    WaterfallFlowLayout *layout = [[WaterfallFlowLayout alloc] init];
    layout.numberOfColumns = 2;      // 设置为2列
    layout.itemSpacing = 10;         // 设置 item 之间的间距
    layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); // 设置 section 的内边距
    
    // 初始化 UICollectionView
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    // 注册 UICollectionViewCell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
    self.collectionView.backgroundColor = [UIColor whiteColor];
    
    // 添加到视图
    [self.view addSubview:self.collectionView];
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 50; // 示例中创建50个 item
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.backgroundColor = [self randomColor]; // 设置随机颜色
    return cell;
}

#pragma mark - Helper Methods

- (UIColor *)randomColor {
    CGFloat red = (CGFloat)arc4random_uniform(256) / 255.0;
    CGFloat green = (CGFloat)arc4random_uniform(256) / 255.0;
    CGFloat blue = (CGFloat)arc4random_uniform(256) / 255.0;
    return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}

@end

        效果如下:

图5.瀑布流效果图

2.UICollectionViewLayout

        UICollectionViewLayout 是 UICollectionView 的核心部分,它定义了 UICollectionView 中所有 item(即 cell)的位置和尺寸。UICollectionViewFlowLayout 是 UICollectionViewLayout 的一个子类,它提供了线性布局、网格布局等常见的布局方式。然而,如果你想要实现自定义布局,则需要继承 UICollectionViewLayout。

1.UICollectionViewLayout 的主要职责

1.确定每个 item 的位置和大小

        通过 layoutAttributesForElementsInRect: 和 layoutAttributesForItemAtIndexPath: 方法确定每个 cell、补充视图、装饰视图的 frame。

2.滚动范围

        collectionViewContentSize 决定了 UICollectionView 可以滚动的区域大小。

3.动画和布局更新

        当布局需要更新时,invalidateLayout 可以触发布局的重新计算,并通过 shouldInvalidateLayoutForBoundsChange: 决定在某些情况下是否需要重新布局。

2.UICollectionViewLayout 的几个关键方法

1.collectionViewContentSize:

        这个方法负责返回 UICollectionView 的内容区域大小,该值控制了内容可以滚动的范围。

- (CGSize)collectionViewContentSize {
    return CGSizeMake(self.collectionView.bounds.size.width, 2000); // 示例: 高度为2000,宽度和 collectionView 一致
}

2.layoutAttributesForElementsInRect:

        这个方法返回当前 rect 区域中所有 item 的布局属性。你需要为给定的区域确定哪些 item 需要展示,并返回它们的 UICollectionViewLayoutAttributes。

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *attributesArray = [NSMutableArray array];
    for (NSInteger i = 0; i < 50; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [attributesArray addObject:attributes];
    }
    return attributesArray;
}

3.layoutAttributesForItemAtIndexPath:

        这个方法返回指定 indexPath 的 item 布局属性。你需要在这里计算每个 item 的位置和大小。

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat itemSize = 100.0; // 固定 item 尺寸
    CGFloat xPosition = (indexPath.item % 3) * (itemSize + 10);
    CGFloat yPosition = (indexPath.item / 3) * (itemSize + 10);
    attributes.frame = CGRectMake(xPosition, yPosition, itemSize, itemSize);
    return attributes;
}
4.invalidateLayout:

        当某些条件改变时,你可以调用 invalidateLayout 来触发重新布局。比如视图的 bounds 发生变化时,可以通过该方法重新计算布局。

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}

5. shouldInvalidateLayoutForBoundsChange:

        返回 YES 表示当 UICollectionView 的 bounds 发生变化时(比如滚动或屏幕旋转)需要重新布局。

3.使用UICollectionViewLayout 实现一个圆形布局

        实现一个圆形布局的步骤如下:

1. 继承 UICollectionViewLayout:自定义布局类。

2. 计算圆形布局的 item 位置和尺寸:每个 item 沿着圆周排列。

3. 提供布局属性:通过 layoutAttributesForElementsInRect: 和 layoutAttributesForItemAtIndexPath: 返回每个 item 的布局信息。

4. 支持滚动:计算内容大小。

        布局代码如下:

#import "IFLYCircleLayout.h"

@implementation IFLYCircleLayout

// 准备布局
- (void)prepareLayout {
    [super prepareLayout];
    
    // 你可以在这里计算 item 的位置等布局信息
}

// 返回内容大小,决定了可以滚动的范围
- (CGSize)collectionViewContentSize {
    return self.collectionView.bounds.size;
}

// 返回给定区域的所有 item 的布局属性
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *attributesArray = [NSMutableArray array];
    
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    
    for (NSInteger i = 0; i < itemCount; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [attributesArray addObject:attributes];
    }
    
    return attributesArray;
}

// 返回某个 item 的布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    // 获取 collectionView 的中心和半径
    CGSize size = self.collectionView.bounds.size;
    CGPoint center = CGPointMake(size.width / 2.0, size.height / 2.0);
    CGFloat radius = MIN(size.width, size.height) / 2.5;
    
    // item 总数
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    
    // 计算每个 item 的角度
    CGFloat angle = (2 * M_PI / itemCount) * indexPath.item;
    
    // 计算 item 的位置
    CGFloat x = center.x + radius * cos(angle);
    CGFloat y = center.y + radius * sin(angle);
    
    attributes.size = CGSizeMake(50, 50); // 设置 item 尺寸
    attributes.center = CGPointMake(x, y); // 设置 item 的中心位置
    
    return attributes;
}

@end

        ViewController关键代码如下:

#import "ViewController.h"
#import "CircleLayout.h"

@interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource>

@property (strong, nonatomic) UICollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化圆形布局
    CircleLayout *layout = [[CircleLayout alloc] init];
    
    // 初始化 UICollectionView
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    self.collectionView.backgroundColor = [UIColor whiteColor];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    // 注册 UICollectionViewCell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"MyCell"];
    
    // 将 UICollectionView 添加到视图
    [self.view addSubview:self.collectionView];
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 12; // 返回 item 数量
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithHue:indexPath.item / 12.0 saturation:1 brightness:1 alpha:1]; // 随机颜色
    cell.layer.cornerRadius = 25; // 圆形
    return cell;
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Selected item at index %ld", (long)indexPath.item);
}

@end

        效果图下:

                图6.UICollectionLayout实现圆形布局

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫柱子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值