封装了一个使用UICollectionViewLayout 实现的吸附居左banner图

首先查看效果图
在这里插入图片描述

实现的原理就是通过自定义UICollectionView layout,然后
设置减速速率是快速就可以达到吸附的效果
_collectionView.decelerationRate = UIScrollViewDecelerationRateFast;

下面贴出所有代码

这里是.h

//
//  LBMiddleExpandLayout.h
//  LiuboMiddleExpandLayout
//
//  Created by liubo on 2023/7/8.
//#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGIN@interface LBMiddleExpandLayout : UICollectionViewFlowLayout@property (nonatomic, assign) BOOL scrollAnimation;/**<是否有分页动画*/
@property (nonatomic, assign) CGPoint lastOffset;/**<记录上次滑动停止时contentOffset值*/- (instancetype)initWithSectionInset:(UIEdgeInsets)insetsandMiniLineSapce:(CGFloat)miniLineSpaceandMiniInterItemSpace:(CGFloat)miniInterItemSpaceandItemSize:(CGSize)itemSize;@endNS_ASSUME_NONNULL_END
//
//  LBMiddleExpandLayout.m
//  LiuboMiddleExpandLayout
//
//  Created by liubo on 2023/7/8.
//#import "LBMiddleExpandLayout.h"@interface LBMiddleExpandLayout ()@property (nonatomic, assign) UIEdgeInsets sectionInsets;
@property (nonatomic, assign) CGFloat miniLineSpace;
@property (nonatomic, assign) CGFloat miniInterItemSpace;
@property (nonatomic, assign) CGSize eachItemSize;@end@implementation LBMiddleExpandLayout/*初始化部分*/
- (instancetype)initWithSectionInset:(UIEdgeInsets)insetsandMiniLineSapce:(CGFloat)miniLineSpaceandMiniInterItemSpace:(CGFloat)miniInterItemSpaceandItemSize:(CGSize)itemSize
{self = [self init];if (self) {//基本尺寸/边距设置self.sectionInsets = insets;self.miniLineSpace = miniLineSpace;self.miniInterItemSpace = miniInterItemSpace;self.eachItemSize = itemSize;}return self;
}- (instancetype)init
{self = [super init];if (self) {_lastOffset = CGPointZero;}return self;
}-(void)prepareLayout
{[super prepareLayout];self.scrollDirection = UICollectionViewScrollDirectionHorizontal;// 水平滚动/*设置内边距*/self.sectionInset = _sectionInsets;self.minimumLineSpacing = _miniLineSpace;self.minimumInteritemSpacing = _miniInterItemSpace;self.itemSize = _eachItemSize;/*** decelerationRate系统给出了2个值:* 1. UIScrollViewDecelerationRateFast(速率快)* 2. UIScrollViewDecelerationRateNormal(速率慢)* 此处设置滚动加速度率为fast,这样在移动cell后就会出现明显的吸附效果*/self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
}/*** 这个方法的返回值,就决定了collectionView停止滚动时的偏移量*/
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{CGFloat pageSpace = [self stepSpace];//计算分页步距CGFloat offsetMax = self.collectionView.contentSize.width - (pageSpace + self.sectionInset.right + self.miniLineSpace);CGFloat offsetMin = 0;/*修改之前记录的位置,如果小于最小contentsize或者大于最大contentsize则重置值*/if (_lastOffset.x<offsetMin){_lastOffset.x = offsetMin;}else if (_lastOffset.x>offsetMax){_lastOffset.x = offsetMax;}CGFloat offsetForCurrentPointX = ABS(proposedContentOffset.x - _lastOffset.x);//目标位移点距离当前点的距离绝对值CGFloat velocityX = velocity.x;BOOL direction = (proposedContentOffset.x - _lastOffset.x) > 0;//判断当前滑动方向,手指向左滑动:YES;手指向右滑动:NOif (offsetForCurrentPointX > pageSpace/8. && _lastOffset.x>=offsetMin && _lastOffset.x<=offsetMax){NSInteger pageFactor = 0;//分页因子,用于计算滑过的cell个数if (velocityX != 0){/*滑动*/pageFactor = ABS(velocityX);//速率越快,cell滑过数量越多}else{/*** 拖动* 没有速率,则计算:位移差/默认步距=分页因子*/pageFactor = ABS(offsetForCurrentPointX/pageSpace);}/*设置pageFactor上限为2, 防止滑动速率过大,导致翻页过多*/pageFactor = pageFactor<1?1:(pageFactor<3?1:2);CGFloat pageOffsetX = pageSpace*pageFactor;proposedContentOffset = CGPointMake(_lastOffset.x + (direction?pageOffsetX:-pageOffsetX), proposedContentOffset.y);}else{/*滚动距离,小于翻页步距一半,则不进行翻页操作*/proposedContentOffset = CGPointMake(_lastOffset.x, _lastOffset.y);}//记录当前最新位置_lastOffset.x = proposedContentOffset.x;return proposedContentOffset;
}/***计算每滑动一页的距离:步距*/
-(CGFloat)stepSpace
{return self.eachItemSize.width + self.miniLineSpace;
}/*** 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局* 一旦重新刷新布局,就会重新调用 layoutAttributesForElementsInRect:方法*/
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{return YES;
}/***防止报错先复制attributes*/
- (NSArray *)getCopyOfAttributes:(NSArray *)attributes
{NSMutableArray *copyArr = [NSMutableArray new];for (UICollectionViewLayoutAttributes *attribute in attributes) {[copyArr addObject:[attribute copy]];}return copyArr;
}/***设置放大动画*/
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{/*获取rect范围内的所有subview的UICollectionViewLayoutAttributes*/NSArray *arr = [self getCopyOfAttributes:[super layoutAttributesForElementsInRect:rect]];/*动画计算*/if (self.scrollAnimation){/*计算屏幕中线*/CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width/2.0f;/*刷新cell缩放*/for (UICollectionViewLayoutAttributes *attributes in arr) {CGFloat distance = fabs(attributes.center.x - centerX);/*移动的距离和屏幕宽度的的比例*/CGFloat apartScale = distance/self.collectionView.bounds.size.width;/*把卡片移动范围固定到 -π/4到 +π/4这一个范围内*/CGFloat scale = fabs(cos(apartScale * M_PI/4));/*设置cell的缩放按照余弦函数曲线越居中越趋近于1*/CATransform3D plane_3D = CATransform3DIdentity;plane_3D = CATransform3DScale(plane_3D, 1, scale, 1);attributes.transform3D = plane_3D;}}return arr;
}@end

使用的地方

//
//  LLLeftPointBannerController.m
//  LiuboMiddleExpandLayout_Example
//
//  Created by liubo on 2023/10/10.
//  Copyright © 2023 liubo. All rights reserved.
//#import "LLLeftPointBannerController.h"
#import "LBMiddleExpandLayout.h"
#import "LBCollectionViewCell.h"@interface LLLeftPointBannerController () <UICollectionViewDataSource, UICollectionViewDelegate>@property (nonatomic, strong) UICollectionView *collectionView;@property (nonatomic, strong) NSMutableArray *signsArray;@end@implementation LLLeftPointBannerController- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor whiteColor];[self.view addSubview:self.collectionView];for (int i = 0; i < 10; i ++) {[self.signsArray addObject:[NSString stringWithFormat:@"%d", i]];}[self.collectionView reloadData];// Do any additional setup after loading the view.
}#pragma mark - UICollectionViewDataSource, UICollectionViewDelegate- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {return 1;
}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return self.signsArray.count;
}- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {//自定义item的UIEdgeInsetsreturn UIEdgeInsetsMake(0, 10 * PLUS_SCALE, 0, 0);
}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {LBCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([LBCollectionViewCell class]) forIndexPath:indexPath];NSString *titleString = self.signsArray[indexPath.item];cell.titleString = titleString;return cell;
}#pragma mark  - lazy load- (UICollectionView *)collectionView
{if (!_collectionView) {LBMiddleExpandLayout *layout = [[LBMiddleExpandLayout alloc] initWithSectionInset:UIEdgeInsetsZeroandMiniLineSapce:15 * PLUS_SCALEandMiniInterItemSpace:15 * PLUS_SCALEandItemSize:CGSizeMake(250 * PLUS_SCALE, 150)];layout.scrollAnimation = NO;_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];_collectionView.delegate = self;_collectionView.dataSource = self;_collectionView.pagingEnabled = NO;_collectionView.decelerationRate = UIScrollViewDecelerationRateFast;_collectionView.showsHorizontalScrollIndicator = NO;_collectionView.backgroundColor = [UIColor clearColor];[_collectionView registerClass:[LBCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([LBCollectionViewCell class])];[_collectionView setFrame:CGRectMake(0,  100, SCREEN_WIDTH,150)];}return _collectionView;
}- (NSMutableArray *)signsArray
{if (!_signsArray) {_signsArray = [NSMutableArray array];}return _signsArray;
}/*
#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

本文demo链接: link

这里是之前写的一个,使用自定义视图实现的,没有使用
UICollectionViewLayout 实现
链接: link

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21429.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《具有源荷不平衡特性的配电网智能软开关和储能联合规划》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

CTF_RE学习

学了一个 map&#xff08;&#xff09;函数的使用 import base64rawData "e3nifIH9b_CndH" target list(map(ord, rawData)) # map 函数将 rawData 中的每个字符传递给 ord 函数。ord 函数返回给定字符的 Unicode 码点 print(target) # 打印 map 对象的内存地址&…

MySQL数据库的约束

MySQL对于数据库存储的数据, 做出一些限制性要求, 就叫做数据库的"约束". 在每一列的 列名, 类型 后面加上"约束". 一. not null (非空) 指定某列不能存储null值. 二. unique (唯一) 保证这一列的每行必须有唯一值. 我们可以看到, 给 table 的 sn 列插…

【微服务】docker部署redis,一主二从三哨兵,读写分离

配置redis读写分离 3台虚拟机 创建目录用于挂载 mkdir -p /root/redis/{conf,data,logs} #master配置文件 bind 0.0.0.0 //任何ip都能访问 port 6379 //redis端口号 logfile "/data/redis.log" //日志文件存放位置&#xff0c;启动redis之前设置为空&#xff…

prometheus docker部署

1.安装Docker sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors":["https://hub-mirror.c.163.com"] } EOF export DOWNLOAD_URL"https://hub-mirror.163.com/docker-ce" curl -fsSL https://ge…

【LIN】STM32新能源汽车LIN通信实现过程

【LIN】STM32新能源汽车LIN通信实现过程 文章目录 前言一、软件二、接线图三、硬件原理图四、上位机五、PICO示波器串行解码1.软件中的LIN波特率设置-192002.PIC设置3.PIC串行解码 六.引用总结 前言 【电机控制】直流有刷电机、无刷电机汇总——持续更新 使用工具&#xff1a;…

godot.bk

1.搜索godot国内镜像&#xff0c;直接安装&#xff0c;mono是csharp版本 2.直接解压&#xff0c;50m&#xff0c;无需安装&#xff0c;直接运行 3.godot里分为场景&#xff0c;节点 主场景用control场景&#xff0c;下面挂textureact放背景图片&#xff0c;右键实例化子场景把…

961题库 北航计算机 计算机网络 附答案 简答题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 某网络拓扑如题下图所示&#xff0c;其中 R 为路由器&#xff0c;主机 H1&#xff5e;H4 的 IP 地址配置以及 R 的各接口 IP 地址配置如图中所示。现有若干以太网交换机…

MySQL—函数—函数小结

一、引言 前面博客我们已经学完了MySQL的函数&#xff0c;下面快速的对MySQL的函数做一个小结。 在讲解了MySQL的函数的时候&#xff0c;主要有四个方面&#xff1a; 1、字符串函数 &#xff08;1&#xff09;CONCAT&#xff1a;字符串连接 &#xff08;2&#xff09;LOWER、…

Java 多线程创建:三种主要方法

多线程编程是Java中一个重要的技术点&#xff0c;它允许程序并行执行多个任务&#xff0c;从而提高程序的执行效率。本文将详细介绍在Java中创建多线程的三种主要方法&#xff1a;继承Thread类、实现Runnable接口以及使用Callable和Future接口。 1. 继承 Thread 类 继承Threa…

Ubuntu server 24 (Linux) IPtables 双网卡 共享上网NAT 安装配置DHCP

一 开启路由转发功能 sudo vim /etc/sysctl.conf net.ipv4.ip_forward1 sudo sysctl -p 二 安装DHCP #更新软件包列表&#xff1a; sudo apt update #安装DHCP服务器 sudo apt install isc-dhcp-server #修改监听网卡,根据实际修改 sudo vi /etc/default/isc-dhcp-server …

配置 HTTP 代理 (HTTP proxy)

配置 HTTP 代理 [HTTP proxy] 1. Proxies2. curl2.1. Environment2.2. Proxy protocol prefixes 3. Use an HTTP proxy (使用 HTTP 代理)3.1. Using the examples (使用示例)3.1.1. Linux or macOS3.1.2. Windows Command Prompt 3.2. Authenticating to a proxy (向代理进行身…

Tailwindcss Layout布局相关样式及实战案例,5万字长文,附完整源码和效果截图

aspect 相关样式类 基础样式 ClassPropertiesaspect-autoaspect-ratio: auto;aspect-squareaspect-ratio: 1 / 1;aspect-videoaspect-ratio: 16 / 9; 案例&#xff1a;引入B站视频 Use the aspect-* utilities to set the desired aspect ratio of an element. 使用’ asp…

两款 IntelliJ IDEA 的 AI 编程插件

介绍两款 IntelliJ IDEA 的 AI 编程插件&#xff1a;通义灵码和 CodeGeeX。 通义灵码 这是由阿里推出的一个基于通义大模型的 AI 编码助手。 它提供了代码智能生成、研发智能问答等功能。通义灵码经过海量优秀开源代码数据训练&#xff0c;可以根据当前代码文件及跨文件的上下…

kafka-偏移量图解

生产者偏移量&#xff1a;生产者发送消息时写入到哪个位置&#xff08;主题的每个分区会存储一个 leo 即将写入消息的偏移量&#xff09;&#xff0c;每次写完消息 leo 会 1 消费者偏移量&#xff1a;消费者从哪个位置开始消费消息&#xff0c;小于等于 leo&#xff0c;每个组…

【Qt】对话框

文章目录 1 :peach:对话框介绍:peach:2 :peach:对话框的分类:peach:2.1 :apple:模态对话框:apple:2.2 :apple:非模态对话框:apple:2.3 :apple:混合属性对话框:apple: 3 :peach:Qt 内置对话框:peach:3.1 :apple:消息对话框 QMessageBox:apple: 1 &#x1f351;对话框介绍&#x…

pod 控制器介绍

一 pod 控制器相关理论介绍 1&#xff0c;Pod控制器 是什么 Pod控制器&#xff0c;又称之为工作负载&#xff08;workload&#xff09;&#xff0c;是用于实现管理pod的中间层&#xff0c;确保pod资源符合预期的状态&#xff0c;pod的资源出现故障时&#xff0c;会尝试进行…

[数据集][目标检测]水下管道泄漏破损检测数据集VOC+YOLO格式2069张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2069 标注数量(xml文件个数)&#xff1a;2069 标注数量(txt文件个数)&#xff1a;2069 标注…

时间序列的谱分解pt.2

16.dvi (berkeley.edu)https://www.stat.berkeley.edu/~bartlett/courses/153-fall2010/lectures/16.pdfpt1 时间序列的谱分解-CSDN博客

新书推荐:1.2 动态链接库与API

本节必须掌握的知识点&#xff1a; kernel32.dll user32.dll gdi32.dll ■动态链接库 最早的软件开发过程&#xff0c;所有的功能实现都是有程序员独立完成的。在这个过程中&#xff0c;我们很快就会发现&#xff0c;有很多常用的功能模块是可以重复利用的&#xff0c;我们将…