UICollectionView详解二:UICollectionViewFlowLayout

2023-12-21 12:58

本文主要是介绍UICollectionView详解二:UICollectionViewFlowLayout,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本人录制技术视频地址:https://edu.csdn.net/lecturer/1899 欢迎观看。

UICollectionViewFlowLayout是一个具体的layout对象,用来把item布局在网格中,并且可选页眉和页脚。在collection view中的items,可以从一行或者一列flow至下一行或者下一列(行或者列取决于滚动的方向)。每行都会根据情况,包含尽可能多的Cells。Cells可以是相同的尺寸,也可以是不同的尺寸。

页眉页脚的属性如下图


当垂直的时候,需要设置Height,如下图


 当水平的时候,需要设置Width,如下图


Section Inset : 我们先通过两个图来看看Sections Insets是怎么回事


 


 从上面的两个图中,我们大概知道了,Section Inset就是某个section中cell的边界范围。

本节中得Demo,就是针对一行进行设计的。效果图如下:



一行显示很多的Icon,并且距中心点越近的Icon,尺寸越大;距离中心点越远的Icon,尺寸逐渐变小。而且在滚动过程中,也是这种效果。

1. 主界面的代码如下(如有不明白的,请参考我上一节讲解的内容,请点击这里):

@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate>
@property (nonatomic,strong) NSMutableArray *images;
@property (nonatomic,weak) UICollectionView *collectionView;
@endstatic NSString *const identifer = @"ImageCell";@implementation ViewController-(NSMutableArray *)images {if (!_images) {_images = [NSMutableArray array];for (int i=1;i<=20;i++) {[_images addObject:[NSString stringWithFormat:@"%d.jpg",i]];}}return _images;
}- (void)viewDidLoad {[super viewDidLoad];CGRect rect = CGRectMake(0, 250, self.view.frame.size.width,200);UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:rect collectionViewLayout:[[LFFlowLayout alloc] init]];collectionView.dataSource = self;collectionView.delegate = self;// 注册collectionView(因为是从xib中加载cell的,所以registerNib)[collectionView registerNib:[UINib nibWithNibName:@"ImageCell" bundle:nil] forCellWithReuseIdentifier:identifer];[self.view addSubview:collectionView];self.collectionView = collectionView;
}#pragma mark - 点击屏幕空白处,切换布局模式
//- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//    if ([self.collectionView.collectionViewLayout isKindOfClass:[LFFlowLayout class]]) {
//        [self.collectionView setCollectionViewLayout:[[LFNormalLayout alloc] init] animated:YES];
//    }
//    else{
//        [self.collectionView setCollectionViewLayout:[[LFFlowLayout alloc] init] animated:YES];
//    }
//
//}#pragma mark - delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return  self.images.count;
}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifer forIndexPath:indexPath];cell.iconName = self.images[indexPath.item];return cell;
}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {// 1. 删除模型数据[self.images removeObjectAtIndex:indexPath.item];// 2. 删除UI元素[collectionView deleteItemsAtIndexPaths:@[indexPath]];
}

2. 自定义一个类,继承自UICollectionViewFlowLayout

@interface LFFlowLayout : UICollectionViewFlowLayout@end

3. 控制布局的主要代码如下,有以下几点,我进行说明:

1) prepareLayout 方法可以对布局中得内容进行初始化工作。如果init方法中执行的代码不起作用,可以放在prepareLayout中。

- (void)prepareLayout {[super prepareLayout];...
}
2)   允许CollectionView Bounds发生变化时,重新进行布局, 要实现下面的方法

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

3) 流式处理中最关键的方法如下,用来获取CollectionView的所有Item项的layout,进行相印的处理。

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {...
}
4) 对于Item的细节计算,可以调用下面的方法,完成定位处理。

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {...
}


完整的实现代码如下:

#define kItemWidth 100#import "LFFlowLayout.h"@implementation LFFlowLayout-(instancetype)init
{if(self=[super init]){}return self;
}- (void)prepareLayout {[super prepareLayout];// 设置为水平滚动self.scrollDirection = UICollectionViewScrollDirectionHorizontal;// 设置每个Item之间的距离self.minimumLineSpacing = 100;// 重新设置Item的尺寸,不然的话,有等比例缩小的可能self.itemSize = CGSizeMake(kItemWidth, kItemWidth);
}#pragma mark - 重写父类的方法
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {return YES;
}// 获取CollectionView的所有Item项,进行相印的处理(移动过程中,控制各个Item的缩放比例)
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {NSArray *array = [super layoutAttributesForElementsInRect:rect];CGFloat inset = (self.collectionView.frame.size.width - kItemWidth) * 0.5;// 设置第一个和最后一个默认居中显示self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);CGRect visibleRect;visibleRect.origin =self.collectionView.contentOffset;visibleRect.size = self.collectionView.frame.size;CGFloat collectionViewCenterX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;for (UICollectionViewLayoutAttributes *attrs in array) {// 只处理正在界面上面显示的Itemif(!CGRectIntersectsRect(visibleRect, attrs.frame)) continue;// 计算各个Item的缩放比例(距离中线越近,缩放比例就越大)CGFloat scale;// 防止突变的情况(当Item的中心与collectionView中心的距离大于等于collectionView宽度的一半时,Item不缩放,平稳过度)if(ABS(attrs.center.x - collectionViewCenterX) >= self.collectionView.frame.size.width * 0.5){scale = 1;}else{scale = 1 + 0.8 * (1 - ABS(attrs.center.x - collectionViewCenterX) / (self.collectionView.frame.size.width * 0.5));}attrs.transform3D = CATransform3DMakeScale(scale, scale, 1);}return array;
}// 当UICollectionView停止的那一刻ContentOffset的值(控制UICollectionView停止时,有一个Item一定居中显示)
// 1.proposedContentOffset默认的ContentOffset
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {//1. 获取UICollectionView停止的时候的可视范围CGRect contentFrame;contentFrame.size = self.collectionView.frame.size;contentFrame.origin = proposedContentOffset;NSArray *array = [self layoutAttributesForElementsInRect:contentFrame];//2. 计算在可视范围的距离中心线最近的ItemCGFloat minCenterX = CGFLOAT_MAX;CGFloat collectionViewCenterX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;for (UICollectionViewLayoutAttributes *attrs in array) {if(ABS(attrs.center.x - collectionViewCenterX) < ABS(minCenterX)){minCenterX = attrs.center.x - collectionViewCenterX;}}//3. 补回ContentOffset,则正好将Item居中显示return CGPointMake(proposedContentOffset.x + minCenterX, proposedContentOffset.y);
}@end


这篇关于UICollectionView详解二:UICollectionViewFlowLayout的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/520086

相关文章

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

详解Java中的敏感信息处理

《详解Java中的敏感信息处理》平时开发中常常会遇到像用户的手机号、姓名、身份证等敏感信息需要处理,这篇文章主要为大家整理了一些常用的方法,希望对大家有所帮助... 目录前后端传输AES 对称加密RSA 非对称加密混合加密数据库加密MD5 + Salt/SHA + SaltAES 加密平时开发中遇到像用户的

Springboot使用RabbitMQ实现关闭超时订单(示例详解)

《Springboot使用RabbitMQ实现关闭超时订单(示例详解)》介绍了如何在SpringBoot项目中使用RabbitMQ实现订单的延时处理和超时关闭,通过配置RabbitMQ的交换机、队列和... 目录1.maven中引入rabbitmq的依赖:2.application.yml中进行rabbit

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输

如何用Java结合经纬度位置计算目标点的日出日落时间详解

《如何用Java结合经纬度位置计算目标点的日出日落时间详解》这篇文章主详细讲解了如何基于目标点的经纬度计算日出日落时间,提供了在线API和Java库两种计算方法,并通过实际案例展示了其应用,需要的朋友... 目录前言一、应用示例1、天安门升旗时间2、湖南省日出日落信息二、Java日出日落计算1、在线API2

使用Spring Cache时设置缓存键的注意事项详解

《使用SpringCache时设置缓存键的注意事项详解》在现代的Web应用中,缓存是提高系统性能和响应速度的重要手段之一,Spring框架提供了强大的缓存支持,通过​​@Cacheable​​、​​... 目录引言1. 缓存键的基本概念2. 默认缓存键生成器3. 自定义缓存键3.1 使用​​@Cacheab