欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > 【iOS】UICollectionView的学习

【iOS】UICollectionView的学习

2025/2/24 0:57:36 来源:https://blog.csdn.net/qq_73106050/article/details/143996751  浏览:    关键词:【iOS】UICollectionView的学习

UICollectionView的学习

文章目录

  • UICollectionView的学习
    • 前言
    • 简单使用UICollectionView
    • 相关的代理方法
    • Layout中常用的一些属性
      • 其他效果实现
    • 瀑布流布局的一个实现
      • 自定义UICollectionViewFlowLayout
      • 实现一个圆环布局
        • VC层中
    • 小结

前言

UICollectionVIew和我们之前的UITableView比较类似,但是UICollectionView可以实现布局的效果,比如说瀑布流布局抑或者是圆环布局,这也是UICollectionView比UITableView强大的地方,这里笔者简单学习了有关于UICollectionView的一些用法,下文笔者来简单介绍一下有关于UICollectionView的一个使用的内容。

简单使用UICollectionView

简单使用UICollectionView主要需要一下几个步骤:

  • 创建一个布局类(这个布局类决定了我们最后的UICollectionView的样式,也可以说是UICollecitonVIew的一个核心内容)。
  • 初始化我的一个UICollecitonView,这里也就是给我们的UICollecitonView设置布局的内容,相当于设置了他的一个布局内容。
  • 实现相关的协议函数。
//View层中的一个内容
UICollectionViewFlowLayout * myLayout = [[UICollectionViewFlowLayout alloc]init];//设置垂直流布局
myLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
myLayout.itemSize = CGSizeMake(100, 100);
self.collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:myLayout];
[self addSubview:self.collectionView];
//控制器中的内容self.iView.collectionView.delegate = self;
self.iView.collectionView.dataSource = self;
[self.iView.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"UICollectionViewCell"];

注意这里一定要进行一个注册cell的内容,否则就会出现问题,这里引用一篇博客的解释:

所以我们可以知道因为UITableView引入的版本较早,当时还未引入注册机制,在那时只需在cellForRowAtIndexPath方法中创建或者重用cell即可,而UICollectionView是在iOS
6.0引入的,相对较新。为了支持更灵活的布局和更复杂的cell类型,Apple在引入时就采用了注册机制。UICollectionView

然后我们就只用执行最后一个步骤,也就是一个实现它要求的一个协议函数,在ViewController层中实现

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return 20;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier: @"UICollectionViewCell" forIndexPath: indexPath];cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];return cell;
}

这里简单解释一下__kindof这个关键字的内容:

__kindof 通常用于泛型(generics)声明,表示该类型是某个类的具体类型或其子类的实例。它在提高代码的灵活性和类型安全方面起到了重要作用,尤其是在使用泛型和集合类时。他保留编译器检查类型的一个能力。

这样我们就初步完成了一个UICollecitonView的使用:

image-20241121201402678

这里在讲解一下原生的垂直布局和水平布局的区别:

这是一个垂直流布局的格式:

image-20241121201615534

下面是水平流布局的一个形式:

image-20241121201919498

两者的区别就在于一个按行填充,一个按列填充。

相关的代理方法

处理用户交互行为的协议,所有方法都是可选的。

  • collectionView:didSelectItemAtIndexPath:
    触发选择单元格的操作。

    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {NSLog(@"选择了分区 %ld 的第 %ld 项", indexPath.section, indexPath.row);
    }
    
  • collectionView:didDeselectItemAtIndexPath:
    触发取消选择的操作(多选模式下有效)。

  1. 高亮效果
    • collectionView:shouldHighlightItemAtIndexPath:
      决定是否允许高亮指定单元格(默认返回 YES)。
    • collectionView:didHighlightItemAtIndexPath:
      用户触摸单元格时调用。
  2. 显示和隐藏
    • collectionView:willDisplayCell:forItemAtIndexPath:
      将要显示某个单元格时调用,可以在这里进行内容更新或动画设置。
    • collectionView:didEndDisplayingCell:forItemAtIndexPath:
      单元格离开屏幕后触发。

Layout中常用的一些属性

主要属性:

  • itemSize:每个单元格的大小。
  • sectionInset:分区的边距。
  • minimumLineSpacing:行间距。
  • minimumInteritemSpacing:列间距。
  • scrollDirection:滚动方向(水平或垂直)。

其他效果实现

有些时候普通的九宫格也不能满足我们的一个需求,我们可以实现一些别的效果:

//设置每个item的大小,双数的为50*50 单数的为100*100
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{if (indexPath.row % 2 != 0) {return CGSizeMake(66, 66);} else {return CGSizeMake(100, 100);}
}

image-20241121205613427

瀑布流布局的一个实现

首先,要实现一个瀑布流的布局我们需要了解UICollectionView的一个布局的一个过程:

image-20241121210434785

从上面这个图中可以看出一个点,也就是我们如果想实现一个瀑布流,应该是我们设计一个自己的layout,然后就可以让我的cell按照我们想要的效果呈现在我们的手机上。

自定义UICollectionViewFlowLayout

首先我们先要自定义一个Layout。

NS_ASSUME_NONNULL_BEGIN
@interface MainPageFlowLayout : UICollectionViewFlowLayout
@property (nonatomic, strong) NSMutableArray* array;
@property (nonatomic, assign) NSInteger itemCount;
@property (nonatomic, assign) NSInteger sectionCount;
@end
NS_ASSUME_NONNULL_END

返回一个瀑布流的效果,其实是主要是重写下面这两个函数

- (void)prepareLayout {
}//只会调用一次这个方法在一开始布局的时候,我们在这个函数把我们的布局设计好,然后将数组元素填充好就可以了。
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {return [NSArray arrayWithArray:self.array];
}//用于返回我们的一个布局数组,我们的cell按照这个要求布局

这里笔者引用一段博客,简单介绍一下perpareLayout中可以做到的一些事情

其中我们在prepareLayout方法进行布局前的准备,其主要有两个作用:
初始化布局参数: 在 prepareLayout 中,你可以进行一些初始化工作,例如计算和缓存用于布局的参数。这可能包括计算每个单元格的大小、计算行列的数量、初始化用于存储布局属性的数据结构等。
计算并缓存布局属性: 你可以在 prepareLayout 中计算并缓存集合视图中所有单元格的布局属性(UICollectionViewLayoutAttributes)。这样,当集合视图需要显示或进行交互时,可以直接访问缓存的布局属性,而不需要在运行时动态计算。UICollectionView

下面来介绍一下实现瀑布流的一个函数:

- (void)prepareLayout {self.array = [NSMutableArray array];[super prepareLayout];//sectionInset是一个UIEdgeInset的类型,主要返回的是组之间的一个间隙CGFloat width = ([UIScreen mainScreen].bounds.size.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing) / 2;//计算每个cell的一个宽度CGFloat colHeight[2] = {self.sectionInset.top, self.sectionInset.bottom};//定义一个浮点型的数组for (int i = 0; i < self.itemCount; i++) { //遍历每一个cellNSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];//获取对应的一个indexPathUICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];//设置一个对应的布局CGFloat height = arc4random() % 123 + 77;//设置高度int indexCol = 0;  //标记短的列if (colHeight[0] < colHeight[1]) {colHeight[0] = colHeight[0] + height + self.minimumLineSpacing;//计算高度的位置indexCol = 0;} else {colHeight[1] = colHeight[1] + height + self.minimumLineSpacing;indexCol = 1;}attributes.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + width) * indexCol, colHeight[indexCol] - height - self.minimumLineSpacing, width, height);//设置布局的一个位置[self.array addObject:attributes];if (colHeight[0] > colHeight[1]) {//找出高度最大的列self.itemSize = CGSizeMake(width, (colHeight[0] - self.sectionInset.top) * 2 / self.itemCount - self.minimumLineSpacing);//计算对应的一个高度的同时取出一个平均值防止数据偏差太大,动态调整大小,同时通过itemSize来决定我们的一个CotentSize的大小,避免这里的滑动问题} else {self.itemSize = CGSizeMake(width, (colHeight[1] - self.sectionInset.top) * 2 / self.itemCount - self.minimumLineSpacing);}}
}
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {return [NSArray arrayWithArray:self.array];
}

这里简单解释一下我们的一个sectionInset的内容这里笔者给出一张图来帮助理解这个属性的内容:

image-20241123160409477

这里我们就实现了一个让瀑布流的一个布局,然后我们在View层中使用这个我们设计的自定义类

- (void)setUI {MainPageFlowLayout* layout = [[MainPageFlowLayout alloc] init];layout.itemCount = 20;layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);self.collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:layout];[self addSubview:self.collectionView];
}

然后就实现了我们的一个瀑布流的一个效果:

image-20241121221556116**

实现一个圆环布局

在上面的参差瀑布流布局的案例中,我们发现如果想要实现一个自定义的一个布局,我们需要创建一个自定义的layout来实现一个布局数组,然后我们的一个cell就会按照上面的内容进行一个布局,所以如果我们想实现一个更高级的布局的话,同样也是采用这个思路。

- (void)prepareLayout {[super prepareLayout];self.itemCount = (NSInteger)[self.collectionView numberOfItemsInSection:0];_attributeArray = [NSMutableArray array];CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height) / 2;CGPoint center = CGPointMake(self.collectionView.frame.size.width / 2, self.collectionView.frame.size.height / 2);for (int i = 0; i < self.itemCount; i++) {NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];UICollectionViewLayoutAttributes* attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];attris.size = CGSizeMake(50, 50);CGFloat x = center.x + cosf(2 * M_PI / self.itemCount * i) * (radius - 25);//计算x轴坐标CGFloat y = center.y + sinf(2 * M_PI / self.itemCount * i) * (radius - 25);//计算y轴坐标attris.center = CGPointMake(x, y);[_attributeArray addObject:attris];}
}-(CGSize)collectionViewContentSize {return self.collectionView.frame.size;
}- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {return [NSArray arrayWithArray:_attributeArray];
}
VC层中
#import "MainPageViewController.h"@interface MainPageViewController ()@end@implementation MainPageViewController- (void)viewDidLoad {[super viewDidLoad];self.iView = [[MainPageView alloc] initWithFrame:self.view.bounds];[self.view addSubview:self.iView];self.iView.collectionView.delegate = self;self.iView.collectionView.dataSource = self;[self.iView.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"UICollectionViewCell"];// Do any additional setup after loading the view.
}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return 11;
}- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier: @"UICollectionViewCell" forIndexPath: indexPath];cell.layer.masksToBounds = YES;cell.layer.cornerRadius = 25;cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];return cell;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {return 1;
}/*
#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {// Get the new view controller using [segue destinationViewController].// Pass the selected object to the new view controller.
}
*/@end

image-20241123154758540

这样就实现了一个圆环形式的一个布局。

小结

笔者简单学习了UICollecitonView这个控件,学习了自定义布局的内容,笔者认为实现瀑布流的核心还是在自定义布局中,笔者也是初次学习UICollecitonVIew这个类如果有什么问题还请不吝指出。

参考博客:

【iOS】UICollecitonVIew

【iOS】UICollecitonVIew的使用

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词