【iOS】—— UICollectionView

news/2024/7/20 22:15:15 标签: ios, ui, android

UICollectionView

文章目录

  • UICollectionView
    • UICollectionView的设计理念
    • UICollectionView的工作流程
    • 和tableView区别
    • collectionView重要的方法
      • **注意!!!**
        • 原因:
      • collectionView需要遵守的协议
    • 然后来看一个最简单应用的demo(==九宫格==):
    • 自定义cell
    • 自定义使用FlowLayout进行灵活布局
      • 🐮1.瀑布式布局
      • 🐮2.圆环式布局

UICollectionView是一个功能十分强大的控件,用法上和UITableView比较相似。

UICollectionView的设计理念

UICollectionView是内容和布局完全分离的设计,UICollectionView负责界面部分,UICollectionViewlayout负责UICollectionView的布局,具体的每个元素的布局就交给UICollectionViewLayoutAttributes,另外attributes也是可以进行扩展的,比如需要加入maskView或者改变layer的属性,都可以在attributes里面进行自己的定义。

UICollectionView的工作流程

当UICollectionView显示内容时,先从数据源获取cell,然后交给UICollectionView。再从UICollectionViewLayout获取对应的layout attributes(布局属性)。最后,根据每个cell对应的layout attributes(布局属性)来对cell进行布局,生成了最终的界面。而用户交互的时候,都是通过Delegate来进行交互。当然,上面只是布局cell,但是UICollectionView内部还有Supplementary View和Decoration View,也可以对其进行布局。

和tableView区别

UICollectionView是比UITableView更加强大的控件,有如下几个方面:

  • 支持水平和垂直两个方向上的布局
  • 通过layout配置方式进行布局
  • CollectionView中的item大小和位置可以自定义
  • 通过layout布局回调的代理方法,可以动态的定制每一个item的大小和collection的大体布局属性
  • 可以完全自定义一套layout布局方案,实现意想不到的效果

collectionView重要的方法

初始化布局:

//layout布局类
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];

设置布局是垂直还是水平:

//布局方向为垂直流布局
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;

设置每个item的大小:

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

collectionView的初始化:

self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];

注册item类型:

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];

注意!!!

tableView的cell可以有注册和不注册两种方法,关于tableView的注册和不注册可以参考—— 自定义cell

但是collectionView只能注册!!!

原因:

iOS6之后的新类,统一从cell复用池获取cell,没有提供返回nil的方式。
UICollectionView的dataSource中的item的注册,itme的注册和tableview中cell的创建是不同的,cell的创建是在cell中,而itme的注册是在viewDidLoad中,因为系统中加载好了tableview后再去加载cell,而如果加载界面时没有item会报错。

collectionView需要遵守的协议

和tableView差不多

UICollectionViewDelegate, UICollectionViewDataSource

🎇UICollectionViewDelegate协议:

是否允许某个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;

然后来看一个最简单应用的demo(九宫格):

//
//  ViewController.m
//  UICollectionView测试
//
//  Created by 翟旭博 on 2022/11/7.
//

#import "ViewController.h"

#define SIZE_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SIZE_HEIGHT ([UIScreen mainScreen].bounds.size.height)

@interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) UICollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor grayColor];    
    //layout布局类
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    //布局方向为垂直流布局
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;    
    //设置每个item的大小
    layout.itemSize = CGSizeMake(SIZE_WIDTH / 3 - 10, SIZE_WIDTH / 3 - 10);    
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];    
    //代理设置
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;    
![请添加图片描述](https://img-blog.csdnimg.cn/63270b47053d4c0a94c3419fb6da965a.png)

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    [self.view addSubview:_collectionView];
}

//返回分区个数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}
//返回每个分区的item个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}
//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {    
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];
    return cell;    
}

@end

请添加图片描述

我们这个设置的大小大约是屏幕三分之一,就会自动的三个item为一行

我们来改一下它的size:

layout.itemSize = CGSizeMake(200, 200);

请添加图片描述

就变成了两个item一行

例如九宫格的例子就可以应用于换头像功能,之前在写其他项目的换头像功能时,运用了循环创建button的方法,在学会了collectionView之后就变得简单了很多。

自定义cell

我们把这个改成换头像功能首先要给cell里面添加东西,这个方法和UITableView的自定义cell基本相同,我们在自定义cell里添加一个属性imageView:

@interface MyCollectionViewCell : UICollectionViewCell

@property (nonatomic, strong) UIImageView *mainImageView;
 
@end

然后在.m里进行初始化和属性设置

#import "MyCollectionViewCell.h"

@implementation MyCollectionViewCell

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    self.mainImageView = [[UIImageView alloc] init];
    [self.contentView addSubview:self.mainImageView];
    
    return self;
}

- (void)layoutSubviews {
    self.mainImageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.width);
}

@end

改变viewController里面的协议函数:

//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    
    //UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"1" forIndexPath:indexPath];
    //cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];
    [cell.mainImageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg", indexPath.row % 4 + 1]]];
    return cell;
    
}

请添加图片描述

自定义使用FlowLayout进行灵活布局

UICollectionView强大的一个原因,就是我们可以制作属于我们的布局,在我们平常刷一些app时,刷新加载的图片大小可能是不确定的,这是使用系统自带的布局是有局限的,所以我们可以自己去创造一个MyLayout类去实现它。
创建一个MyLayout类继承UICollectionViewFlowLayout.

简单来说,自定义一个FlowLayout布局类就是两个步骤:

  • 设计好我们的布局配置数据,prepareLayout方法中
  • 返回我们的配置数组,layoutAttributesForElementsInRect方法中

🐮1.瀑布式布局

//
//  MyLayout.m
//  UICollectionView高级
//
//  Created by 翟旭博 on 2022/11/7.
//
#import "MyLayout.h"
@implementation MyLayout
//数组的相关设置在这个方法中
//布局前的准备 会调用这个方法
- (void)prepareLayout {
    [super prepareLayout];    
    _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
    _attributeArray = [[NSMutableArray alloc] init];    
    //设置为静态的2列
    //计算每一个item的宽度
    float itemWidth = ([UIScreen mainScreen].bounds.size.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing ) / 2;
    
    //定义数组保存每一列的高度
    //这个数组的主要作用是保存每一列的总高度,这个样在布局时,我们可以始终将下一个item放在最短的列下面
    CGFloat colHeight[2] = {self.sectionInset.top, self.sectionInset.bottom};    
    //itemCount是外界传进来的item的个数 遍历来设置每一个item的布局    
    for (int i = 0; i < self.itemCount; i++) {
        //设置每一个item的位置等相关属性
        NSIndexPath *index = [NSIndexPath indexPathForItem:i inSection:0];
        //创建一个布局属性类, 通过indexPath来创建
        UICollectionViewLayoutAttributes *attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
        //随意一个高度 在50-250之间
        CGFloat height = arc4random() % 200 + 50;       
        //哪一行高度小 则放到哪一列下面
        //标记最短的列
        int flag = 0;
        if (colHeight[0] < colHeight[1]) {
            //将新的item高度加入到短的一列
            colHeight[0] = colHeight[0] + height + self.minimumLineSpacing;
            flag = 0;
        } else {
            colHeight[1] = colHeight[1] + height + self.minimumLineSpacing;
            flag = 1;
        }        
        //设置item的位置
        attris.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + itemWidth) * flag, colHeight[flag] - height - self.minimumLineSpacing, itemWidth, height);        
        [_attributeArray addObject:attris];
    }    
    //设置itemSize来确保滑动范围的正确 这里是通过将所有的item高度平均化,计算出来的 (以最高的列为标准)
    if (colHeight[0] > colHeight[1]) {
        self.itemSize = CGSizeMake(itemWidth, (colHeight[0] - self.sectionInset.top) * 2 / _itemCount - self.minimumLineSpacing);
    } else {
        self.itemSize = CGSizeMake(itemWidth, (colHeight[1] - self.sectionInset.top) * 2 / _itemCount - self.minimumLineSpacing);
    }    
}
//返回布局数组
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return _attributeArray;
}

@end


    // layout布局
    MyLayout *layout = [[MyLayout alloc] init];
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];

请添加图片描述

🐮2.圆环式布局

//
//  CircleLayout.m
//  UICollectionView高级
//
//  Created by 翟旭博 on 2022/11/7.
//

#import "CircleLayout.h"

@implementation CircleLayout

- (void)prepareLayout {
    
    [super prepareLayout];
    
    //获取item个数
    _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
    _attributeArray = [[NSMutableArray alloc] init];
    
    //先设定大圆的半径 取长和宽最短的
    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);
    
    //设置每个item的大小为50 * 50,半径为25
    for (int i = 0; i < _itemCount; i++) {
        UICollectionViewLayoutAttributes *attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        
        //设置item大小
        attris.size = CGSizeMake(100, 100);
        
        //计算每个item的圆心位置
        float x = center.x + cosf(2 * M_PI / _itemCount * i) * (radius - 50);
        float y = center.y + sinf(2 * M_PI / _itemCount * i) * (radius - 50);
    
        attris.center = CGPointMake(x, y);
        [_attributeArray addObject:attris];
    }
}

//设置内容区域大小
- (CGSize)collectionViewContentSize {
    return self.collectionView.frame.size;
}

//返回设置数组
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return _attributeArray;
}

@end


请添加图片描述


http://www.niftyadmin.cn/n/4165.html

相关文章

量化股票查询代码是什么?

量化股票查询代码是什么&#xff1f;接下来用一些代码来分析一下&#xff0c;如下&#xff1a; 做空95&#xff1a;HHV((HIGHLOWOPEN2*CLOSE)/5H-L,5),COLORBLUE;做空68: HHV((HIGH-LOWOPEN2*CLOSE)/5*2-L,5),COLORRED&#xff1b; 平衡点&#xff1a;LLV((HIGHLOWOPEN2*CLOSE…

Python中的几种推导式用法,先收藏再说......

嗨害大家好鸭&#xff01;我是小熊猫❤ 今天我们来整点非常干的干货&#xff1a; 源码、资料电子书点击此处 Python 推导式是一种独特的数据处理方式&#xff0c; 可以从一个数据序列构建另一个新的数据序列的结构体。 Python 支持各种数据结构的推导式&#xff1a; 列表(li…

[附源码]计算机毕业设计JAVAjsp毕业设计管理系统

[附源码]计算机毕业设计JAVAjsp毕业设计管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM myb…

基础IO(下)——Linux

文章目录1. 理解文件系统1.2 背景知识1.2 inode vs 文件名1.3 软硬链接2. 动态库和静态库2.1 静态库.a2.1.1 如果想写一个库&#xff1f;&#xff08;编写库的人的角度&#xff09;2.1.2如果我把库给别人&#xff0c;别人怎么用呢&#xff1f;&#xff08;使用库的人的角度&…

JavaScript | 1000个判断条件难道要写了1000个 if ? 一文教你如何实现分支优化

&#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;大三学生&#xff0c;一个不甘平庸的平凡人&#x1f36c; &#x1f5a5;️ NodeJS专栏&#xff1a;Node.js从入门到精通 &#x1f5a5;️ 博主的前端之路&#xff08;源创征文一等奖作品&#xff09;&#xff1a;前端之行&am…

JUC五-ForkJoin,JMM和volatile,CAS,自旋锁,死锁

ForkJoin 翻译&#xff1a;分支合并 ForkJoin在JDk1.7,并行执行任务!提高效率,数据量大! 大数据中的一个概念: Map Reduce&#xff0c;把大任务拆分为小任务. ForkJoin的 特点: 工作窃取 这个里面维护的都是双端队列 b线程执行完了 去帮a执行一个任务 具体使用&#xff1a;…

QT Android环境搭建 及 解决“Platfrom tools installed”等系列配置问题( 附QT、JDK、SDK、NDK网盘链接 )

文章目录一、安装Android相关插件二、添加JDK、SDK、NDK路径三、搭建环境过程常见问题1. “Platfrom tools installed”失败2. 以上操作全部完成且无报错现象&#xff0c;Android编译就是报错3. “SDK manger is not available with the current version of SDK tools. Use nat…