可以使用 UICollectionViewCompositionalLayout 制作此布局

Can make this layout with a UICollectionViewCompositionalLayout

我正在尝试使用 UICollectionViewCompositionalLayout

创建一个 'tag cloud' 部分

我想要一个全宽的项目部分,然后是一个以这种方式包装的部分

基本人力资源财务状况员工敬业度都将是部分。请忽略其他 UI 个元素。

此部分还需要支持多行,因为可能有 1 到 15 个标签。

我已经尝试过了,但无法创建那种环绕效果,每个项目都没有宽度,使用每个项目的 intrinsicContentSize

我目前的尝试是

import UIKit

final class CustomCell: UICollectionViewCell {
  let label = UILabel(frame: .zero)

  override init(frame: CGRect) {
    super.init(frame: frame)
    label.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(label)
    NSLayoutConstraint.activate([
      label.topAnchor.constraint(equalTo: contentView.topAnchor),
      label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
      label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
      label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
    ])
  }

  required init?(coder: NSCoder) {
    return nil
  }

}


protocol SectionData {
  var text: String { get }
}

struct DummyData: SectionData {
  let text: String
}

enum SectionType: Int, CaseIterable {
  case single
  case double
  case carousel
  case tags
}

struct Section {
  let id: Int
  let type: SectionType
  let title: String?
  let subtitle: String?
  let data: [SectionData]
}

class ViewController: UIViewController {

  private var items: [Section] = [] {
    didSet { collectionView.reloadData() }
  }

  private(set) lazy var collectionView: UICollectionView = {
    let collectionView = UICollectionView(frame: view.frame, collectionViewLayout: makeLayout())
    collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    collectionView.backgroundColor = .systemBackground
    collectionView.dataSource = self
    collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "CustomCell")
    return collectionView
  }()

  override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(collectionView)

    items = [
      Section(id: 0, type: .single, title: nil, subtitle: nil, data: Array(0...3).map { index in DummyData(text: "Item \(index)") }),
      Section(id: 1, type: .tags, title: nil, subtitle: nil, data: Array(0...15).map { index in DummyData(text: "Item \(index)") })
    ]

  }
}

extension ViewController: UICollectionViewDataSource {

  func numberOfSections(in collectionView: UICollectionView) -> Int {
    return items.count
  }

  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return items[section].data.count
  }

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let model = items[indexPath.section].data[indexPath.item]
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
    cell.backgroundColor = indexPath.item % 2 == 0 ? .darkGray : .lightGray
    cell.label.text = model.text
    return cell
  }

}


extension ViewController {

  func makeLayout() -> UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { [weak self] index, env in
      guard let self = self else { return nil }
      let section = self.items[index]
      switch section.type {
        case .single: return self.makeSingleSection()
        case .tags: return self.makeTagSection(count: section.data.count)
        default: return nil
      }
    }

    let config = UICollectionViewCompositionalLayoutConfiguration()
    config.interSectionSpacing = 20
    layout.configuration = config
    return layout
  }

  func makeSingleSection() -> NSCollectionLayoutSection {
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
    let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)
    let layoutGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(100))
    let layoutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitems: [layoutItem])
    let layoutSection = NSCollectionLayoutSection(group: layoutGroup)
    return layoutSection
  }

  func makeTagSection(count: Int) -> NSCollectionLayoutSection {
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
    let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)
    let layoutGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(100))
    let layoutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitem: layoutItem, count: count)
    let layoutSection = NSCollectionLayoutSection(group: layoutGroup)
    return layoutSection
  }
}

如果能帮助我了解如何实现此布局,我将不胜感激。

您可以创建该部分布局,但是您的工厂方法中存在一些错误。

像这样会产生你想要的效果-

  func makeTagSection(count: Int) -> NSCollectionLayoutSection {
    let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(100), heightDimension: .absolute(32))
    let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)

    let layoutGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: itemSize.heightDimension)
    let layoutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitems: [layoutItem])

    layoutGroup.interItemSpacing = .fixed(8)

    let layoutSection = NSCollectionLayoutSection(group: layoutGroup)
    layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
    layoutSection.interGroupSpacing = 8
    return layoutSection
  }