当前位置: 首页 > news >正文

「Swift」类淘宝商品瀑布流展示

前言:需要做一个类似于淘宝商品页面的瀑布流展示
结构分析:


ps:图片来源

思路分析:

该瀑布流主要还是基于UICollectionView进行展示,只是在cell展示的UICollectionViewFlowLayout需要进行相应调整和自定义,所以需要自己创建一个FlowLayout,该FlowLayout是基于UICollectionViewFlowLayout实现的,所以该FlowLayout的初始化调用流程于系统没有区别,只需遵循WaterfallMutiSectionDelegate代理。

1.初始化:
let layout = ZUPowerShopProductLayout()
layout.delegate = self
myCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
2.Cell代理:
1.必须实现的代理方法
/// collectionItem高度
func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat
2.可选择的代理方法
/// 每个section 列数(默认2列)
@objc optional func columnNumber(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> Int/// header高度(默认为0)
@objc optional func referenceSizeForHeader(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> CGSize/// footer高度(默认为0)
@objc optional func referenceSizeForFooter(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> CGSize/// 每个section 边距(默认为0)
@objc optional func insetForSection(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> UIEdgeInsets/// 每个section item上下间距(默认为0)
@objc optional func lineSpacing(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> CGFloat/// 每个section item左右间距(默认为0)
@objc optional func interitemSpacing(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> CGFloat/// section头部header与上个section尾部footer间距(默认为0)
@objc optional func spacingWithLastSection(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, section: Int) -> CGFloat
3.自定义的FlowLayout文件
import UIKit@objc protocol WaterfallMutiSectionDelegate: NSObjectProtocol {// 必选delegate实现/// collectionItem高度func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat// 可选delegate实现/// 每个section 列数(默认2列)@objc optional func columnNumber(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> Int/// header高度(默认为0)@objc optional func referenceSizeForHeader(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGSize/// footer高度(默认为0)@objc optional func referenceSizeForFooter(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGSize/// 每个section 边距(默认为0)@objc optional func insetForSection(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> UIEdgeInsets/// 每个section item上下间距(默认为0)@objc optional func lineSpacing(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGFloat/// 每个section item左右间距(默认为0)@objc optional func interitemSpacing(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGFloat/// section头部header与上个section尾部footer间距(默认为0)@objc optional func spacingWithLastSection(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGFloat
}class WaterfallMutiSectionFlowLayout: UICollectionViewFlowLayout {weak var delegate: WaterfallMutiSectionDelegate?private var sectionInsets: UIEdgeInsets = .zeroprivate var columnCount: Int = 2private var lineSpacing: CGFloat = 0private var interitemSpacing: CGFloat = 0private var headerSize: CGSize = .zeroprivate var footerSize: CGSize = .zero//存放attribute的数组private var attrsArray: [UICollectionViewLayoutAttributes] = []//存放每个section中各个列的最后一个高度private var columnHeights: [CGFloat] = []//collectionView的Content的高度private var contentHeight: CGFloat = 0//记录上个section高度最高一列的高度private var lastContentHeight: CGFloat = 0//每个section的header与上个section的footer距离private var spacingWithLastSection: CGFloat = 0override func prepare() {super.prepare()self.contentHeight = 0self.lastContentHeight = 0self.spacingWithLastSection = 0self.lineSpacing = 0self.sectionInsets = .zeroself.headerSize = .zeroself.footerSize = .zeroself.columnHeights.removeAll()self.attrsArray.removeAll()let sectionCount = self.collectionView!.numberOfSections// 遍历sectionfor idx in 0..<sectionCount {let indexPath = IndexPath(item: 0, section: idx)if let columnCount = self.delegate?.columnNumber?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.columnCount = columnCount}if let inset = self.delegate?.insetForSection?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.sectionInsets = inset}if let spacingLastSection = self.delegate?.spacingWithLastSection?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.spacingWithLastSection = spacingLastSection}// 生成headerlet itemCount = self.collectionView!.numberOfItems(inSection: idx)let headerAttri = self.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: indexPath)if let header = headerAttri {self.attrsArray.append(header)self.columnHeights.removeAll()}self.lastContentHeight = self.contentHeight// 初始化区 y值for _ in 0..<self.columnCount {self.columnHeights.append(self.contentHeight)}// 多少个itemfor item in 0..<itemCount {let indexPat = IndexPath(item: item, section: idx)let attri = self.layoutAttributesForItem(at: indexPat)if let attri = attri {self.attrsArray.append(attri)}}// 初始化footerlet footerAttri = self.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, at: indexPath)if let footer = footerAttri {self.attrsArray.append(footer)}}}override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {return self.attrsArray}override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {if let column = self.delegate?.columnNumber?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.columnCount = column}if let lineSpacing = self.delegate?.lineSpacing?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.lineSpacing = lineSpacing}if let interitem = self.delegate?.interitemSpacing?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.interitemSpacing = interitem}let attri = UICollectionViewLayoutAttributes(forCellWith: indexPath)let weight = self.collectionView!.frame.size.widthlet itemSpacing = CGFloat(self.columnCount - 1) * self.interitemSpacinglet allWeight = weight - self.sectionInsets.left - self.sectionInsets.right - itemSpacinglet cellWeight = allWeight / CGFloat(self.columnCount)let cellHeight: CGFloat = (self.delegate?.heightForRowAtIndexPath(collectionView: self.collectionView!, layout: self, indexPath: indexPath, itemWidth: cellWeight))!var tmpMinColumn = 0var minColumnHeight = self.columnHeights[0]for i in 0..<self.columnCount {let columnH = self.columnHeights[i]if minColumnHeight > columnH {minColumnHeight = columnHtmpMinColumn = i}}let cellX = self.sectionInsets.left + CGFloat(tmpMinColumn) * (cellWeight + self.interitemSpacing)var cellY: CGFloat = 0cellY = minColumnHeightif cellY != self.lastContentHeight {cellY += self.lineSpacing}if self.contentHeight < minColumnHeight {self.contentHeight = minColumnHeight}attri.frame = CGRect(x: cellX, y: cellY, width: cellWeight, height: cellHeight)self.columnHeights[tmpMinColumn] = attri.frame.maxY//取最大的for i in 0..<self.columnHeights.count {if self.contentHeight < self.columnHeights[i] {self.contentHeight = self.columnHeights[i]}}return attri}override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {let attri = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: indexPath)if elementKind == UICollectionView.elementKindSectionHeader {if let headerSize = self.delegate?.referenceSizeForHeader?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.headerSize = headerSize}self.contentHeight += self.spacingWithLastSectionattri.frame = CGRect(x: 0, y: self.contentHeight, width: self.headerSize.width, height: self.headerSize.height)self.contentHeight += self.headerSize.heightself.contentHeight += self.sectionInsets.top} else if elementKind == UICollectionView.elementKindSectionFooter {if let footerSize = self.delegate?.referenceSizeForFooter?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {self.footerSize = footerSize}self.contentHeight += self.sectionInsets.bottomattri.frame = CGRect(x: 0, y: self.contentHeight, width: self.footerSize.width, height: self.footerSize.height)self.contentHeight += self.footerSize.height}return attri}override var collectionViewContentSize: CGSize {return CGSize(width: self.collectionView!.frame.size.width, height: self.contentHeight)}
}
实现效果图

在这里插入图片描述

该文章参考借鉴了iOS 多section瀑布流实现(swift)文章,由此篇文章内容受益匪浅!

借鉴文章作者的github demo:https://github.com/RoganZheng/WaterfallMultiSectionFlowLayout,如果该demo对你有帮助的话,记得帮这位作者star一下

如果该文章对你有所帮助的话,可以点赞、收藏并关注一下!后续会持续更新更多技术内容

相关文章:

「Swift」类淘宝商品瀑布流展示

前言&#xff1a;需要做一个类似于淘宝商品页面的瀑布流展示 结构分析&#xff1a; ps&#xff1a;图片来源 思路分析&#xff1a; 该瀑布流主要还是基于UICollectionView进行展示&#xff0c;只是在cell展示的UICollectionViewFlowLayout需要进行相应调整和自定义&#xff…...

道可云会展元宇宙平台全新升级,打造3D沉浸式展会新模式

随着VR虚拟现实、人工智能、虚拟数字人等元宇宙技术的快速发展&#xff0c;各个行业正试图通过元宇宙技术寻求新的发展突破口&#xff0c;会展行业也不例外。会展作为经贸领域的重要产业形态&#xff0c;越来越多的企业和组织开始寻求通过元宇宙技术为展会赋能&#xff0c;以满…...

Ant Design Pro初始化报错

今天按照官网步骤初始化项目&#xff0c;第一次报错 fatal: unable to access https://github.com/ant-design/ant-design-pro/: SSL certificate problem: unable to get local issuer certificate 致命&#xff1a;无法访问https://github.com/ant-design/ant-design-pro/&…...

第16届中国R会议暨2023X-AGI大会开幕,和鲸科技分享ModelOps在数据科学平台中的实践与应用

11月25日&#xff0c;第 16 届中国 R 会议暨 2023 X-AGI 大会在在中国人民大学逸夫会堂拉开帷幕&#xff0c;本次会议由中国人民大学统计学院、中国人民大学应用统计科学研究中心、统计之都、原灵科技和中国商业统计学会人工智能分会&#xff08;筹&#xff09;主办&#xff0c…...

❀My学习Linux命令小记录(12)❀

目录 ❀My学习Linux命令小记录&#xff08;12&#xff09;❀ 46.arp指令 47.tcpdump指令 48.chmod指令 49.chown指令 50.bash调用脚本指令 shell介绍 shell脚本的组成部分 脚本执行方式 检查脚本语法 bash之变量 变量的种类&#xff1a;根据生效的范围不同来区分 …...

MySQL学习day05

DCL&#xff08;Data Control Language&#xff09;数据控制语言学习 作用&#xff1a;用来创建数据库用户、控制数据库的访问权限 1&#xff09;查询用户&#xff1a; use mysql; select * from user; 2&#xff09;创建用户&#xff1a; create user 用户名主机名 identifi…...

JAVA面试题7

1.Java中的ClassLoader是什么&#xff1f; 它有什么作用&#xff1f; 答案&#xff1a;ClassLoader是一种加载Java类文件的机制&#xff0c;可以从不同的来源加载类文件&#xff0c;如本地文件系统、网络等。ClassLoader可以帮助实现模块化开发和动态加载类等功能。 2.什么是J…...

好用免费的AI换脸5个工具

在当今社会的发展中&#xff0c;人工智能&#xff08;Artificial Intelligence, AI&#xff09;扮演着关键的角色&#xff0c;其应用领域不断扩展。作为AI的一个分支&#xff0c;换脸技术近年来备受欢迎。这项技术使得将一个人的面部特征迁移到另一个人的照片或视频成为可能。除…...

【Linux】公网远程访问AMH服务器管理面板

目录 1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装Cpolar4. 配置AMH面板公网地址5. 远程访问AMH面板6. 固定AMH面板公网地址 AMH 是一款基于 Linux 系统的服务器管理面板&#xff0c;它提供了一系列的功能&#xff0c;包括网站管理、FTP 管理、数据库管理、DNS 管理、…...

随笔-这都是命吗

我与鹏哥、小付有个小群&#xff0c;前几天&#xff0c;鹏哥在群里发了一个图&#xff0c;是他那个城市准备扶持的高新产业&#xff0c;有元宇宙、量子信息、生物制药、人工智能什么的。 先前的时候鹏哥给我说过&#xff0c;当地准备了六百多亩地&#xff0c;准备发展高新产业…...

优化网站性能,从容谈CDN加速的部署与运维

随着互联网的迅猛发展&#xff0c;网站的性能优化成为网站运维工作中不可或缺的一环。其中&#xff0c;CDN&#xff08;Content Delivery Network&#xff09;加速技术因其在全球范围内提供快速、可靠的内容分发而备受关注。本文将从一个网站运维的角度出发&#xff0c;深入探讨…...

JavaScript-事件

事件 事件流 指的是事件完整执行过程中的流动路径 两个阶段&#xff1a; 捕获阶段&#xff1a;从大到小冒泡阶段&#xff1a;从小到大 实际开发中都是使用事件冒泡为主 事件捕获 从DOM的根元素开始取执行对应的事件&#xff08;从外到里&#xff09; document.addEventLis…...

linux的磁盘管理

Linux 提供了多种工具和技术来进行磁盘管理。下面是对 Linux 磁盘管理的详细解释&#xff1a; 磁盘和分区&#xff1a; 磁盘&#xff08;硬盘&#xff09;&#xff1a;Linux 系统中的磁盘通常是通过 SATA、SCSI、NVMe 等接口连接的物理硬盘。可以使用工具如 lsblk、fdisk、pa…...

qt-C++笔记之主线程中使用异步逻辑来处理ROS事件循环和Qt事件循环解决相互阻塞的问题

qt-C笔记之主线程中使用异步逻辑来处理ROS事件循环和Qt事件循环解决相互阻塞的问题 code review! 文章目录 qt-C笔记之主线程中使用异步逻辑来处理ROS事件循环和Qt事件循环解决相互阻塞的问题1.Qt的app.exec()详解2.ros::spin()详解3.ros::AsyncSpinner详解4.主线程中结合使用…...

【Docker】从零开始:18.使用Dockerfile构造自己的KingbaseES数据库镜像

【Docker】从零开始&#xff1a;17.使用Dockerfile构造自己的数据库镜像 新建一个自定义目录并创建Dockerfile文件上传需要的文件到自定义目录下注意docker-circle-init.sh文件内容password 内容 开始打包注意打包完成后执行 尝试用工具连接数据库 kingbase.tar.gz 包过大我就上…...

YOLOv8独家改进《全网无重复 YOLOv8专属打造》感知聚合SERDet检测头:简单高效涨点,即插即用|检测头新颖改进

💡本篇内容:YOLOv8独家改进《全网无重复,YOLOv8专属》感知聚合SERDet检测头:高效涨点,即插即用|检测头新颖改进 💡🚀🚀🚀本博客 YOLO系列 + 全新原创感知聚合SERDet检测头 改进创新点改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可,附改进源代…...

Android Studio中Flutter项目找不到Android真机设备解决方法

起因&#xff1a;创建正常Android项目可以运行在真机设备上&#xff0c;创建flutter项目就找寻不到Android真机设备。 1&#xff1a;在flutter sdk安装目录按下Shift和鼠标右键&#xff0c;打开Powershell窗口 2&#xff1a;输入以下&#xff0c;然后回车 flutter config --…...

Vue 静态渲染 v-pre

v-pre 指令&#xff1a;用于阻止 Vue 解析这个标签&#xff0c;直接渲染到页面中。 语法格式&#xff1a; <div v-pre> {{ 数据 }} </div> 基础使用&#xff1a; <template><h3>静态渲染 v-pre</h3><p v-pre>静态渲染&#xff1a;{{ n…...

C语言基础概念考查备忘 - 标识符、关键字、预定义标识符、语法检查、语义检查 ... 左值、右值、对象、副作用、未定义行为、sizeof是什么等等

什么是标识符、关键字和预定义标识符&#xff1f;三者有何区别&#xff1f; 当谈论C语言中的标识符、关键字和预定义标识符时&#xff0c;让我们从每个概念的基础开始。 标识符&#xff08;Identifiers&#xff09;&#xff1a; 标识符是用来给变量、函数、类型等命名的。在…...

插件原理与开发

插件原理与开发 在 Mybatis总体执行流程 一文中简单的介绍了插件的初始化过程&#xff0c;本文将从源码的角度介绍一下mybatis的插件原理与简单开发实战。 插件原理 插件的注册和管理是通过InterceptorChain进行的&#xff0c;在创建Executor、StatementHandler、ParameterH…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...