麻花痒 发表于 2024-6-28 10:24:48

【iOS】UICollectionView

前言

UICollectionView是iOS6之后引入的一个新的UI控件,它和UITableView有着诸多的相似之处,其中很多代理方法都非常雷同。简单来说,UICollectionView是比UITbleView更加强盛的一个UI控件,有如下几个方面:
1、支持程度与垂直结构
2、通过layout设置的方式进行结构
3、CollectionView中Item的巨细与位置可以自由定义
4、通过layout结构回调的代理方法,可以动态的定制每个item的巨细和collection的大体结构属性
5、更加强盛一点,完全自定义一套layout结构方案,可以实现意想不到的效果
一、实现简单九宫格结构

我们先来看一下简单九宫格的实现效果:
https://img-blog.csdnimg.cn/direct/861487ce7aa24740b7564842b16995cf.png
实现步调:
1、创建结构类并为其各个属性赋值
2、将自定义的结构类作为参数传递给UICollectionView的初始化方法- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER;
3、设置代理,实现协议函数
//创建一个layout布局类
UICollectionViewFlowLayout * layout = [init];
//设置布局方向为垂直流布局
layout.scrollDirection = UICollectionViewScrollDirectionVertical;

//设置每个item的大小为100*100
layout.itemSize = CGSizeMake(100, 100);
//创建collectionView 通过一个布局策略layout来创建
UICollectionView * collect = [initWithFrame:self.view.frame collectionViewLayout:layout];
//代理设置
collect.delegate=self;
collect.dataSource=self;
//注册item类型 这里使用系统的类型
forCellWithReuseIdentifier:@"cellid"];

   
;
这里有一点需要留意,collectionView在完成代理回调前,必须注册一个cell,雷同如下:
forCellWithReuseIdentifier:@"cellid"];
这便是collectionView与tableView不同的一点,tableView除了注册cell的方法外,还可以通过临时创建来做:
//tableView在从复用池中取cell的时候,有如下两种方法
//使用这种方式如果复用池中无,是可以返回nil的,我们在临时创建即可
- (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
//6.0后使用如下的方法直接从注册的cell类获取创建,如果没有注册 会崩溃
- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);
在UICollectionView中,注册cell是必须的,由于UICollectionView没有默认的cell范例,而是完全依靠于开辟者提前注册cell类或nib以供后续利用。
在UITableView中,历史上早期版本的iOS SDK并没有引入注册机制。在那些版本中,你只需在cellForRowAtIndexPath:代理方法中创建或重用cell即可。而且如果你没有注册cell,UITableView会尝试利用默认的UITableViewCell来呈现内容。
   以是我们可以知道由于UITableView引入的版本较早,当时还未引入注册机制,在当时只需在cellForRowAtIndexPath方法中创建或者重用cell即可,而UICollectionView是在iOS
6.0引入的,相对较新。为了支持更灵活的结构和更复杂的cell范例,Apple在引入时就采取了注册机制。
上面的设置完成后我们再实现几个与tableview雷同的代理方法
另外有一点细节:
我们在设置结构方式的时候设置了垂直结构:
layout.scrollDirection = UICollectionViewScrollDirectionVertical;

//这个是水平布局
//layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
这样可以实现当我们的第一行充满后会主动进行第二行的结构
二、UICollectionView中的常用方法和属性

1.UICollectionViewFlowLayout相干属性

(1)初始化cell样式
UICollectionViewFlowLayout *layout = [ init];
(2)结构类的方向
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
   UICollectionViewScrollDirectionVertical 竖向排布
UICollectionViewScrollDirectionHorizontal 横向排布
(3)设置item的巨细
layout.itemSize = CGSizeMake(120, 100);
(4)设置item的上下间距
layout.minimumLineSpacing = 100;
(5)设置item的左右间距
layout.minimumInteritemSpacing = 10;
(6)设置item的内间距
layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
2.UICollectionView相干属性

(1)UICollectionView实例化对象和巨细
self.collectionView = [ initWithFrame:self.view.frame collectionViewLayout:layout];
(2)革新对应的列
[self.collectionView performBatchUpdates:^{
    ];
} completion:nil];
三、协媾和代理方法:

(1)遵守的协议:
<UICollectionViewDelegate, UICollectionViewDataSource>
(2)代理方法:
UICollectionViewDelegate协议
该协议用来设置和处理collectionView的功能和一些逻辑,全部的方法都是可选实现。
是否允许某个item的高亮,返回NO,则不能进入高亮状态


[*](BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
当item高亮时触发的方法
[*](void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
竣事高亮状态时触发的方法
[*](void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath;
是否可以选中某个item,返回NO,则不能选中
[*](BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
是否可以取消选中某个item
[*](BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
已经选中某个item时触发的方法
[*](void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
取消选中某个item时触发的方法
[*](void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
将要加载某个item时调用的方法
[*](void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);
将要加载头尾视图时调用的方法
[*](void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);
已经展示某个item时触发的方法
[*](void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;
已经展示某个头尾视图时触发的方法
[*](void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
UICollectionView进行重新结构时调用的方法
[*](nonnull UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout;
UICollectionViewDataSource协议
必须实现的方法:
设置每个分区的item数


[*](NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
设置返回每个item的属性
[*](UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
可选实现的方法:
设置分区数,固然这个方法是可选的,一样平常我们都会去实现,不去设置它的话,它的默认值为1
[*](NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
对头视图或者尾视图进行设置
[*](UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
设置某个item是否可以被移动,返回NO则不能移动
[*](BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
移动item的时候,会调用这个方法
[*](void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath;
四、九宫格式的结构进行升级

偶尔候一个简单的九宫格并不能满意我们的需求,偶尔我们需要item展现不同的巨细
//设置每个item的大小,双数的为50*50 单数的为100*100
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    if (indexPath.row%2!=0) {
      return CGSizeMake(50, 50);
    }else{
      return CGSizeMake(100, 100);
    }
}
https://img-blog.csdnimg.cn/direct/6836337e3f99473baa124b3b4e288273.png
五、实现瀑布流结构

实现思绪

UIcollectionView的精髓是UICollectionViewLayout,UICollectionViewLayout决定了我们的UIcollectionView是怎样显现在我们的界面上的,为了实现我们的自定义瀑布流式结构,我们需要在子类里重写生成结构的方法,创建我们自己需要的结构
实现原理

   在实现该功能之前,我们先相识一下 UICollectionView 的结构过程,它与结构对象之间的关系是一种协作的关系,当
UICollectionView 需要一些结构信息的时候,它会去调用结构对象的一些函数,这些函数的执行是有肯定的次序的,如图所示:https://img-blog.csdnimg.cn/direct/6e7b84ff14a64b13aea37b6fce43b409.png
以是我们的创建的继承自UICollectionViewLayout的子类实现以下方法:
- (void)prepareLayout;//进行提前创建布局
- (CGSize)collectionViewContentSize//返回内容画布的大小
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
//返回rect中所有元素的布局属性,返回的是一个数组
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
//返回对应的indexPath的位置的cell的布局属性。
   重写 - (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;方法返回对应indexPath的位置的追加视图的结构属性,如果没有就不消重载
重写 - (nullable UICollectionViewLayoutAttributes )layoutAttributesForDecorationViewOfKind:(NSString)elementKind atIndexPath:(NSIndexPath *)indexPath;方法返回对应indexPath的位置的装饰视图的结构属性,如果没有也不需要重载
重写 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;当边界发生改变时,是否应该革新。
由此我们可以知道如果我们想要实现自定义瀑布流,必须需要对以下方法进行重写
https://img-blog.csdnimg.cn/direct/327a785c379a4d269e2c9b2275a6289f.png
其中我们在prepareLayout方法进行结构前的准备,其重要有两个作用:
初始化结构参数: 在 prepareLayout 中,你可以进行一些初始化工作,例如盘算和缓存用于结构的参数。这大概包括盘算每个单位格的巨细、盘算行列的数目、初始化用于存储结构属性的数据结构等。
盘算并缓存结构属性: 你可以在 prepareLayout 中盘算并缓存聚集视图中全部单位格的结构属性(UICollectionViewLayoutAttributes)。这样,当聚集视图需要显示或进行交互时,可以直接访问缓存的结构属性,而不需要在运行时动态盘算。
而- (CGSize)collectionViewContentSize决定了聚集视图可以滚动的范围,即聚集视图可以在其内容地区内滚动的最大范围。在自定义结构中,你需要根据结构的详细逻辑来盘算内容尺寸。这个尺寸通常是在全部的结构属性(UICollectionViewLayoutAttributes)被盘算之后确定的,以确保可以或许容纳全部的元素。
(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect方法则是用于返回在给定矩形地区内的全部视图的结构属性(UICollectionViewLayoutAttributes 实例)数组
代码

下面给出各个函数的代码:
prepareLayout:
//数组的相关设置在这个方法中
//布局前的准备 会调用这个方法
- (void)prepareLayout {
    ;
    self.attributeArray = ; // 使用实例变量,而不是全局变量

   
    // 设置为静态的2列
    // 计算每一个item的宽度
    CGFloat itemWidth = (self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing) / 2;

    // 定义数组保存每一列的高度
    // 这个数组的主要作用是保存每一列的总高度,这个样在布局时,我们可以始终将下一个item放在最短的列下面
    // prepareLayout 中
    self.columnHeights = ;

    // itemCount是外界传进来的item的个数 遍历来设置每一个item的布局
    for (int i = 0; i < _itemCount; i++) {
      // 设置每一个item的位置等相关属性
      NSIndexPath *index = ;
      // 创建一个布局属性类, 通过indexPath来创建
      UICollectionViewLayoutAttributes *attris = ;
      // 随意一个高度 在60-260之间
      CGFloat height = arc4random() % 200 + 60;

      // 哪一行高度小 则放到哪一列下面
      // 标记最短的列
      NSInteger shortestColumn = doubleValue] < doubleValue] ? 0 : 1;

      // 将新的item高度加入到短的一列
      _columnHeights = @( doubleValue] + height + self.minimumLineSpacing);

      // 设置item的位置
      attris.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + itemWidth) * shortestColumn, doubleValue] - height - self.minimumLineSpacing,itemWidth, height);

      ;
    }
这里的sectionInset的top与bottom让笔者理解了一段时间,这里用一张图解释一下:
https://img-blog.csdnimg.cn/direct/ea6b4f8bd5b94acebd23ae19da700ec0.png
(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect:
//返回布局数组
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return _attributeArray;
}
- (CGSize)collectionViewContentSize:
- (CGSize)collectionViewContentSize {
    // collectionView的contentSize的高度等于所有列高度中最大的值
    CGFloat maxColumnHeight = doubleValue];
    for (NSInteger i = 1; i < 2; i++) {
      CGFloat columnHeight = doubleValue];
      if (maxColumnHeight < columnHeight) {
            maxColumnHeight = columnHeight;
      }
      NSLog(@"%f", maxColumnHeight);
    }
    return CGSizeMake(0, maxColumnHeight);
}
(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath:
//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {


    UICollectionViewCell *cell = ;
   
    cell.backgroundColor = ;
    return cell;

}
调用顺序

对于UICollectionView,函数调用顺序为

[*]prepareLayout:
调用时机: 在结构发生变化之前,比如初始化时、调用 reloadData、invalidateLayout 等方法时。
作用: 进行一些结构的准备工作。
[*]collectionViewContentSize:
调用时机: 在需要盘算聚集视图内容尺寸时,比如初始化时、结构发生变化时。
作用: 返回整个聚集视图内容的尺寸。
[*]layoutAttributesForElementsInRect::
调用时机: 在需要显示或更新某个地区内的视图时,比如滚动时、结构发生变化时。
作用: 返回给定矩形地区内全部视图的结构属性。
[*](UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
调用时机:创建item时调用
作用:创建和设置单位格
其中prepareLayout方法只会调用一次,之后在往下滑动UICollectionView时会不停重复调用其他三种方法
相较于仅能纵向单列数据展示的tableView,UICollectionView允许我们进行更加自由且灵活的结构,而轻松实现这种效果则归功于我们的自定义的UICollectionViewFlowLayout类,我们可以通过重写此类中的一些方法来实现我们单位格的多样结构。
实现步调

由此我们得出自定义结构的实现步调:

[*]创建自定义结构类UICollectionViewFlowLayout
[*]重写结构方法,确定结构数组_attributeArray(重要)
[*]注册自定义结构类 MyCustomFlowLayout *customLayout = [ init]; ;
[*]创建cell
实现效果

https://img-blog.csdnimg.cn/direct/d6bd2c04a6e94eb191da60a76db2b986.png
总结

通过学习UICollectionView,笔者认为最重要的便是知道我们的UICollectionView是怎样去创建我们的单位格的:
通过利用默认或是自定义的流结构UICollectionViewLayout来实现我们的灵活结构,这便需要我们先去实现我们的结构属性数组,确定怎样结构后程序会去创建我们的cell,然后将cell逐个填入我们的结构数组中=
笔者在这里仅仅浮浅地学习了UICollectionView,如有不敷请不吝指出,而对于UICollectionView的焦点,笔者认为其为确定结构

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【iOS】UICollectionView