iOS左对齐自动换行collection样式

前言

想必大家工作中或多或少会遇到下图样式的UI需求吧

像这种cell长度不固定,以此向右对齐排列的样式UI可以说是很常见的

实现方式

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

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

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

主要代码如下:

swift 复制代码
/// 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%
swift 复制代码
/// 用字典存储已经计算过的cell item,常见的以空间换时间方式
private lazy var calculatedAttrs = [IndexPath: UICollectionViewLayoutAttributes]()
  • 在使用的时候,只需要将collection.collectionViewLayout的属性设置为我们自定义的layout对象即可,具体代码如下面的示例代码:
swift 复制代码
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就行了,具体示例代码如下:
swift 复制代码
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上了,可以直接拿来使用

开源代码地址

相关推荐
代码的小搬运工1 小时前
【iOS】3G-Share仿写总结
macos·ios·cocoa
2501_916007472 小时前
iOS和macOS应用程序性能分析和优化工具使用综合指南
android·macos·ios·小程序·uni-app·iphone·webview
智购科技无人售货机工厂3 小时前
自动售货机硬件选型避坑:单片机 / 树莓派 / ESP32 怎么选?~YH
人工智能·单片机·嵌入式硬件·r语言·swift·perl·symfony
游戏开发爱好者83 小时前
iOS开发IDE有哪些 Xcode 和 快蝎 轻量替代方案
ide·vscode·ios·个人开发·xcode·swift·敏捷流程
ZZH_AI项目交付1 天前
同一套前摄心率算法,为什么 iPhone 16 Pro 稳,iPhone XR 会失准?
ios·app·ai编程
DeMinds1 天前
内容没有丢,我为什么总在重新整理?|DeMinds 如何让工作接着继续
ios·github·markdown
apd_csdn1 天前
清华邮箱苹果邮件app设置(全)
ios·thu
MDM.Plus1 天前
从“遥控”到“自治”:苹果 MDM 技术的代际跨越与业务重构
ios·智能手机·重构·mdm
黑化旺仔1 天前
iOS - 天气预报仿写总结
ios
Coffeeee1 天前
ios零基础的Android开发能否靠AI让老板省一笔人工费呢
android·ios·ai编程