Ric*_*oon 11 uicollectionview uicollectionviewcompositionallayout
我有一个UICollectionViewLayout包含三列的网格。列中的每个项目都有一个充满文本的单元格。我希望所有列的高度与组中最高的项目相同。使用UICollectionViewCompositionalLayout我很难获得想要的结果。
关于如何实现这一目标有什么想法吗?全部代码如下。功能createLayout是最相关的。请注意,我正在使用LoremSwiftum生成随机文本。
预期行为:
我能够通过将 更改NSCollectionLayoutSize为绝对值 500 来模拟这种预期行为。这不是我想要的。每个单元格应该估计其大小,并使用该组的最大值。
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(500))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(500))
Run Code Online (Sandbox Code Playgroud)
整个代码:
import UIKit
import LoremSwiftum
class ViewController: UIViewController {
enum Section {
case main
}
var dataSource: UICollectionViewDiffableDataSource<Section, Int>! = nil
var collectionView: UICollectionView! = nil
let sectionBackgroundDecorationElementKind = "section-background-element-kind"
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Two-Column Grid"
configureHierarchy()
configureDataSource()
}
/// - Tag: TwoColumn
func createLayout() -> UICollectionViewLayout {
let spacing = CGFloat(10)
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(44))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 3)
group.interItemSpacing = .fixed(spacing)
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = spacing
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
func configureHierarchy() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = .systemBackground
view.addSubview(collectionView)
}
func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<TextCell, Int> { (cell, indexPath, identifier) in
// Populate the cell with our item description.
cell.label.text = Lorem.sentences(Bool.random() ? 1 : 3)
}
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
// Return the cell.
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
}
// initial data
var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
snapshot.appendSections([.main])
snapshot.appendItems(Array(0..<94))
dataSource.apply(snapshot, animatingDifferences: false)
}
}
class TextCell: UICollectionViewCell {
let label = UILabel()
let bottomLabel = UILabel()
let container = UIView()
static let reuseIdentifier = "text-cell-reuse-identifier"
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("not implemented")
}
func configure() {
label.numberOfLines = 0
label.adjustsFontForContentSizeCategory = true
label.font = UIFont.preferredFont(forTextStyle: .title1)
label.backgroundColor = .systemPurple
label.textColor = .white
bottomLabel.numberOfLines = 0
bottomLabel.adjustsFontForContentSizeCategory = true
bottomLabel.font = UIFont.preferredFont(forTextStyle: .title1)
bottomLabel.text = "Bottom of cell"
bottomLabel.backgroundColor = .systemRed
bottomLabel.textColor = .white
let stack = UIStackView(arrangedSubviews: [label, bottomLabel])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.distribution = .equalSpacing
stack.spacing = 20
contentView.addSubview(stack)
backgroundColor = .systemBlue.withAlphaComponent(0.75)
let inset = CGFloat(10)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: inset),
stack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -inset),
stack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: inset),
stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -inset),
])
}
}
Run Code Online (Sandbox Code Playgroud)
我最终不得不创建一个UICollectionViewCompositionalLayout子类,但该解决方案效果非常好。
使用新的子类,在单元格中保留对布局的弱引用,以便您可以在preferredLayoutAttributesFitting.
class EqualHeightsUICollectionViewCompositionalLayout: UICollectionViewCompositionalLayout {
private var heights = [Int: [IndexPath: CGFloat]]()
private var largests = [Int: CGFloat]()
private let columns: Int
init(section: NSCollectionLayoutSection, columns: Int) {
self.columns = columns
super.init(section: section)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
if #unavailable(iOS 15) {
if let attributes = attributes {
for attribute in attributes {
updateLayoutAttributesHeight(layoutAttributes: attribute)
}
}
}
return attributes
}
override func invalidateLayout() {
super.invalidateLayout()
heights.removeAll(keepingCapacity: true)
largests.removeAll(keepingCapacity: true)
}
func updateLayoutAttributesHeight(layoutAttributes: UICollectionViewLayoutAttributes) {
let height = layoutAttributes.frame.height
let indexPath = layoutAttributes.indexPath
let row = indexPath.item / columns
heights[row]?[indexPath] = height
largests[row] = max(largests[row] ?? 0, height)
let size = CGSize(width: layoutAttributes.frame.width,
height: largests[row] ?? 0)
layoutAttributes.frame = .init(origin: layoutAttributes.frame.origin, size: size)
}
}
Run Code Online (Sandbox Code Playgroud)
class TextCell: UICollectionViewCell {
let label = UILabel()
weak var layout: EqualHeightsUICollectionViewCompositionalLayout?
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemBlue.withAlphaComponent(0.75)
configure()
}
required init?(coder: NSCoder) {
fatalError("not implemented")
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let attribute = super.preferredLayoutAttributesFitting(layoutAttributes)
layout?.updateLayoutAttributesHeight(layoutAttributes: attribute)
return attribute
}
private func configure() {
let bottomLabel = UILabel()
label.numberOfLines = 0
label.font = UIFont.preferredFont(forTextStyle: .title1)
label.backgroundColor = .systemPurple
label.textColor = .white
label.setContentHuggingPriority(.defaultHigh, for: .vertical)
bottomLabel.numberOfLines = 0
bottomLabel.font = UIFont.preferredFont(forTextStyle: .title1)
bottomLabel.text = "Bottom of cell"
bottomLabel.backgroundColor = .systemRed
bottomLabel.textColor = .white
bottomLabel.setContentHuggingPriority(.defaultLow, for: .vertical)
let stackView = UIStackView(arrangedSubviews: [label, bottomLabel])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 20
contentView.addSubview(stackView)
let padding: CGFloat = 15
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding),
])
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1804 次 |
| 最近记录: |