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

news/2024/7/20 20:52:58 标签: swift, 开发语言, ios
前言:需要做一个类似于淘宝商品页面的瀑布流展示
结构分析:


ps:图片来源

思路分析:

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

1.初始化:
swift">let layout = ZUPowerShopProductLayout()
layout.delegate = self
myCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
2.Cell代理:
1.必须实现的代理方法
swift">/// collectionItem高度
func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: ZUPowerShopProductLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat
2.可选择的代理方法
swift">/// 每个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文件
swift">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 = .zero
  private var columnCount: Int = 2
  private var lineSpacing: CGFloat = 0
  private var interitemSpacing: CGFloat = 0
  private var headerSize: CGSize = .zero
  private 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 = 0
  
  
  override func prepare() {
    super.prepare()
    self.contentHeight = 0
    self.lastContentHeight = 0
    self.spacingWithLastSection = 0
    self.lineSpacing = 0
    self.sectionInsets = .zero
    self.headerSize = .zero
    self.footerSize = .zero
    self.columnHeights.removeAll()
    self.attrsArray.removeAll()
    
    let sectionCount = self.collectionView!.numberOfSections
    // 遍历section
    for 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
      }
      // 生成header
      let 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)
      }
      // 多少个item
      for 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)
        }
      }
      
      // 初始化footer
      let 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.width
    let itemSpacing = CGFloat(self.columnCount - 1) * self.interitemSpacing
    let allWeight = weight - self.sectionInsets.left - self.sectionInsets.right - itemSpacing
    let cellWeight = allWeight / CGFloat(self.columnCount)
    let cellHeight: CGFloat = (self.delegate?.heightForRowAtIndexPath(collectionView: self.collectionView!, layout: self, indexPath: indexPath, itemWidth: cellWeight))!
    
    var tmpMinColumn = 0
    var minColumnHeight = self.columnHeights[0]
    for i in 0..<self.columnCount {
      let columnH = self.columnHeights[i]
      if minColumnHeight > columnH {
        minColumnHeight = columnH
        tmpMinColumn = i
      }
    }
    let cellX = self.sectionInsets.left + CGFloat(tmpMinColumn) * (cellWeight + self.interitemSpacing)
    var cellY: CGFloat = 0
    cellY = minColumnHeight
    if 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.spacingWithLastSection
      attri.frame = CGRect(x: 0, y: self.contentHeight, width: self.headerSize.width, height: self.headerSize.height)
      self.contentHeight += self.headerSize.height
      self.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.bottom
      attri.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一下

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


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

相关文章

2024年天津中德应用技术大学专升本专业课报名及考试时间通知

天津中德应用技术大学2024年高职升本科专业课报名确认及考试通知 按照市高招办《2024年天津市高职升本科招生实施办法》&#xff08;津招办高发〔2023〕14号&#xff09;文件要求&#xff0c;天津中德应用技术大学制定了2024年高职升本科专业课考试报名、确认及考试实施方案&a…

Unity加载配置文件【解析Json】

Json 文件 Json文件的存储&#xff1a; 存储在StreamingAssets目录下的&#xff1a;//这里用了游戏配置表常用的Json存储格式-对象数组 {"data":[{"id": 1001,"name": "ScreenFront_1",},{"id": 1002,"name": &…

iOS ------ UICollectionView

一&#xff0c;UICollectionView的简介 UICollectionView是iOS6之后引入的一个新的UI控件&#xff0c;它和UITableView有着诸多的相似之处&#xff0c;其中许多代理方法都十分类似。简单来说&#xff0c;UICollectionView是比UITbleView更加强大的一个UI控件&#xff0c;有如下…

selenium中元素定位正确但是操作失败,6种解决办法全搞定

selenium中元素定位正确但是操作失败的原因无外乎以下4种&#xff1a; 01 页面没加载好 解决方法&#xff1a;添加等待方法&#xff0c;如&#xff1a;time.sleep() 02 页面提交需要等待给数据后台 解决方法&#xff1a;添加等待方法&#xff0c;如&#xff1a;time.sleep(…

【算法思考记录】力扣2824. 统计和小于目标的下标对数目C++

Problem: 2824. 统计和小于目标的下标对数目 文章目录 问题关键举例说明&#xff1a;解题思路C代码 问题关键 虽然题目要求 i < j&#xff0c;但其实对索引的限制没有这么大&#xff01; 因为&#xff0c;题目只是要求找下标对的数量&#xff0c;我们不需要输出原数组的下…

【跨境营商】创新科技助力数码转型 增强大湾区企业核心竞争力

粤港澳大湾区作为国家的重点发展区域&#xff0c;坐拥丰富的资源及商机&#xff0c;企业积极推行数码化&#xff0c;务求在大湾区抢占先机。香港电讯商业客户业务董事总经理吴家隆表示&#xff0c;近年企业锐意加快数码化步伐&#xff0c;香港电讯以创新科技融入的数码方案&…

【五分钟】熟练使用numpy.cumsum()函数(干货!!!)

引言 numpy.cumsum()函数用于计算输入数组的累积和。当输入是多维数组时&#xff0c;numpy.cumsum()函数可以沿着指定轴计算累积和。 计算一维数组的累计和 代码如下&#xff1a; # 计算一维数组的累计和 tmp_array np.ones((4,), dtypenp.uint8) # [1, 1, 1, 1] print(&…