iOS左对齐自动换行collection样式

news2025/2/26 22:25:09

前言

想必大家工作中或多或少会遇到下图样式的UI需求吧
左对齐样式UI
像这种cell长度不固定,以此向右对齐排列的样式UI可以说是很常见的

实现方式

一般的实现可能主要是分一下两种:

  • 1、一种是用button依次排列实现,动态计算text宽度,记录之前一个button的位置,和当前button的宽度,看是否最终会超出屏幕的右边,一旦超出右边,就换行到下一行
    • 缺点
      • 当数据量多的时候,生成很多的button,不能对button进行重用
      • 每次生成一个button的时候都要计算位置,相对较麻烦
    • 优点
      • 适合数据少的情况
  • 2、采用collection view,依次从左到右进行布局排列cell
    • 优点
      • 数据量大的时候,能重用cell,减少cell数量,增高渲染性能
      • 省去每次cell布局的位置计算
      • 代码复用,实现一个UICollectionViewFlowLayout的子类,拿到哪儿都能用
实现

我们这里实现主要采用第二种方式,实现的方式是自定义一个UICollectionViewFlowLayout的子类,在这个类里对cell布局进行排列

主要代码如下:

/// main method for layout cell
/// - Parameter indexPath: indexpath
/// - Returns: layouted UICollectionViewLayoutAttributes
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    if let attr = calculatedAttrs[indexPath] { return attr }

    guard let curAttr = super.layoutAttributesForItem(at: indexPath) else { return nil }

    if isHorizontal {
      	// 如果滚动方向是水平的话,就直接返回。这里的水平布局主要适合不换行的那种
        calculatedAttrs[indexPath] = curAttr
        return curAttr
    }

  	// 下面主要针对滚动方式是垂直方向的进行布局,因为实际开发中,绝大部分情况也是垂直滚动方向
    let sectionInset = calculateSectionInsetForItem(at: indexPath.section)
    let layoutWidth = collectionView?.frame.width ?? 0 - sectionInset.left - sectionInset.right
  
    if indexPath.item == 0 {
      	// 如果是当前section的第一个元素,就直接设置x为sectionInset.left
        curAttr.frame.origin.x = sectionInset.left
        calculatedAttrs[indexPath] = curAttr
        return curAttr
    }
		
  	// 计算非第一个元素的frame布局
    let prevIP = IndexPath(item: indexPath.item - 1, section: indexPath.section)
    let prevRect = layoutAttributesForItem(at: prevIP)?.frame ?? .zero
    let prevRectRightPoint = prevRect.origin.x + prevRect.size.width
    let stretchedCurRect = CGRect(x: sectionInset.left,
                                  y: curAttr.frame.origin.y,
                                  width: layoutWidth,
                                  height: curAttr.frame.size.height)

    if !prevRect.intersects(stretchedCurRect) {
        curAttr.frame.origin.x = sectionInset.left
        calculatedAttrs[indexPath] = curAttr
        return curAttr
    }

    curAttr.frame.origin.x = prevRectRightPoint + calculateMinimumInteritemSpacingForSection(at: indexPath.section)
    calculatedAttrs[indexPath] = curAttr
    return curAttr
}
  • 这里我对水平滚动方向也进行了适配,不过水平滚动方向主要适用于不换行的那种从左到右依次排列的样式,比如如下示例图:

在这里插入图片描述

  • 由于每次重用cell的时候,会再次重复计算cell的frame,为了减少重复冗余的计算,我进行了如下的性能优化
    • 这是常见的以空间换时间的解决方式
    • 经测试,这样子优化后,性能提升了将近90%
/// 用字典存储已经计算过的cell item,常见的以空间换时间方式
private lazy var calculatedAttrs = [IndexPath: UICollectionViewLayoutAttributes]()
  • 在使用的时候,只需要将collection.collectionViewLayout的属性设置为我们自定义的layout对象即可,具体代码如下面的示例代码:
private lazy var collectionView: UICollectionView = {
  // instance ZLCollectionLeftAlignLayout
  let defaultLayout = ZLCollectionLeftAlignLayout()
  defaultLayout.minimumLineSpacing = 10.0
  defaultLayout.minimumInteritemSpacing = 10.0
  defaultLayout.scrollDirection = .vertical
  defaultLayout.sectionInset = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 20.0, right: 10.0)
  // set collectionViewLayout to a instance of ZLCollectionLeftAlignLayout
  let collectionView = UICollectionView(frame: .zero, collectionViewLayout: defaultLayout)
  collectionView.backgroundColor = .magenta
  collectionView.showsVerticalScrollIndicator = false
  return collectionView
}()
  • 之后就是在collection view的代理方法中设置每个cell的size就行了,具体示例代码如下:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 
  let w = CGFloat.random(in: 20.0 ... 50.0)
  return CGSize(width: 30.0 + w, height: 25.0)
}
开源代码地址

我已经将代码开源到github上了,可以直接拿来使用,喜欢的小伙伴记得点个赞哦

开源代码地址

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

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

相关文章

【数据结构初阶】二、 线性表里的顺序表

相关代码gitee自取: C语言学习日记: 加油努力 (gitee.com) 接上期: 【数据结构初阶】一. 复杂度讲解_高高的胖子的博客-CSDN博客 1 . 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实…

Top 15 开源3D分子蛋白质建模与渲染软件

如今,WebGL 是一种趋势技术,因为它允许开发人员使用现代浏览器作为客户端来创建复杂的 3D 交互式图形、游戏,而无需安装额外的插件、扩展或软件。 WebGL允许浏览器直接与GPU(图形处理单元)一起工作。 推荐:…

Java中使用JTS实现WKT字符串读取转换线、查找LineString的list中距离最近的线、LineString做缓冲区扩展并计算点在缓冲区内的方位角

场景 Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等): Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等)_jts-core_霸道流氓气质的博客-CSDN博客 JavaGeoTools实现WKT数据根据EPSG编码进行坐标系转换&…

分布式、锁、延时任务

1. redission 2.zk 2.1 指令 ls / / 下有哪些子节点 get /zookeeper 查看某个子节点内容 create /aa “test” delete /aa set /aa “test01” 2.2 创建节点 模式 默认创建永久 create -e 创建临时 create -e /zz “hello zz” create -s 创建 有序节点 create -s -e 临…

测试平台项目部署一(手动部署)

手动部署 一、项目框架图1、首先创建一个桥接网络:2、redis3、启动mariadb4、跨域配置5、JWT配置6、celery配置7、启动ck14_django 容器8、安装gunicorn9、数据库迁移10、创建用户11、添加工作进程12、验证异步执行任务、定时执行任务通过二、supervisor1、安装2、创建配置文件…

开源|HDR-ISP开源项目介绍

引言 拖更很久了,本着出品必精的原则,我们更新就来点干货。想起刚入行时,网上并没有很多以及系统的ISP的学习资料,都是边工作、边搜集资料然后边学习,一路坎坎坷坷走到今天算是刚入了ISP的大门。 为了解决新人入门的…

openGauss学习笔记-63 openGauss 数据库管理-资源池化架构

文章目录 openGauss学习笔记-63 openGauss 数据库管理-资源池化架构 openGauss学习笔记-63 openGauss 数据库管理-资源池化架构 本文档主要介绍资源池化架构下的一些最佳实践和使用注意事项,用于支撑对相关特性感兴趣的开发者可以快速部署、实践或进行定制化开发。…

【时空融合:改进MRA】

Multiresolution Analysis Pansharpening Based on Variation Factor for Multispectral and Panchromatic Images From Different Times (基于变化因子的多光谱和全色图像多分辨率分析) 大多数泛锐化方法是将同一区域上同时获取的原始低分辨率多光谱(M…

Redis的数据持久化方案

目录 前言 RDB方式 概述: 1.RDB手动 2.RDB自动 RDB优缺点 AOF方式 概述 AOF写数据的三种策略 AOF相关配置 AOF重写 AOF重写方式 手动重写 bgrewriteaof 自动重写 总结 前言 Redis是一个内存型数据库,也就是说如果不将内存中的…

overleaf 参考文献引用,创建引用目录.bib文件,在文档中引用参考文献,生成参考文献列表

目录 1 创建一个Overleaf项目 2 导入或创建 .bib 文件 2.1 导入 .bib 文件: 参考文献的 .bib文件获取步骤 (1)打开谷歌学术 (2)输入文献题目 (3)点击引用,然后选择BibTex格式…

af-table-column插件的使用 element el-table-column宽度自适应

af-table-column 是一个用于 Vue.js 的表格列组件,用于在表格中定义列的样式和行为。下面是 af-table-column 的使用方法: 首先,确保已经安装了 af-table-column 包。可以使用以下命令进行安装: npm install af-table-column --…

MySQL——事务

一、事务的开始与结束 一个数据库事务由一条或多条sql语句构成,它们形成一个逻辑的工作单元。这些sql语句要么全部执行成功,要么全部执行失败。 1.1.事物的开始 1.对于DDL(create,alter,drop)和DCL&…

render函数使用和详解

背景 在平时编程时,大部分是通过template来创建html。但是在一些特殊的情况下,使用template方式时,就无法很好的满足需求,在这个时候就需要 通过JavaScript 的编程能力来进行操作。此时,就到了render函数展示拳脚去时…

【Python】迭代器__iter__、__next__

这里主要纠正迭代器的用法,因为一些教程传播错误示例让我很无语。 最大的错误就是,把__iter__和__next写在同个类里,每每看见都感到诧异。不是说这方法不行,主要是,一旦出现预期之外的运行结果往往很难查到原因(因为它…

Nomad 系列-Nomad+Traefik+Tailscale 集成实现零信任安全

系列文章 Nomad 系列文章Traefik 系列文章Tailscale 系列文章 概述 终于到了令人启动的环节了:NomadTraefikTailscale 集成实现零信任安全。 在这里: Nomad 负责容器调度;(容器编排工具)Traefik 负责入口流量&…

文件导入之Validation校验List对象数组

背景: 我们的接口是一个List对象,对象里面的数据基本都有一些基础数据校验的注解,我们怎么样才能校验这些基础规则呢? 我们在导入excel文件进行数据录入的时候,数据录入也有基础的校验规则,这个时候我们又…

Linux下C语言使用 netlink sockets与内核模块通信

netlink简介 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。在Linux标准内核中,系统默认集成了很多netlink实例,比如日志上报、路由系统等,netlink消息是双向的&a…

解决 tesserocr报错 Failed to init API, possibly an invalid tessdata path : ./

问题描述 我们在初次使用tesserocr库的时候,可能会报以下错误: RuntimeError: Failed to init API, possibly an invalid tessdata path: ./ 这是因为我们在使用 conda 创建的环境中找不到"tessdata"这个文件夹。 解决办法 这时候把Tessera…

【CMake工具】工具CMake编译轻度使用(C/C++)

目录 CMake编译工具 一、CMake概述 二、CMake的使用 2.1 注释 2.1.1 注释行 2.1.2 注释块 2.2 源文件 2.1.1 共处一室 2.1.2 VIP包房 2.3 私人定制 2.2.1 定义变量 2.2.2 指定使用的C标准 2.2.3 指定输出的路径 2.4 搜索文件 2.3.1 方式1 2.3.2 方式2 2.5 包含…

CRM软件系统能否监控手机的使用

CRM可以监控手机吗?答案是不可以。CRM是一款帮助企业优化业务流程,提高销售效率的工具。例如Zoho CRM,最多也就是听一下销售的通话录音,却不可以监控手机,毕竟CRM不是一款监控软件。 CRM的主要作用有以下几点&#xf…