import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView: UICollectionView!
let colors: [UIColor] = [.red, .green, .gray, .yellow, .orange, .purple, .brown, .blue, .cyan]
var columnWidths = [CGFloat]()
var numberOfRows = 30
override func viewDidLoad() {
super.viewDidLoad()
// 创建和配置自定义布局
let layout = CustomFlowLayout()
layout.columnWidths = [100, 150, 80, 120, 200] // 示例宽度,根据需要设置
layout.numberOfRows = numberOfRows
layout.itemHeight = 50
// layout.scrollDirection = .horizontal
// layout.sectionHeadersPinToVisibleBounds = true
columnWidths = layout.columnWidths
// 初始化UICollectionView
collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.bounces = false
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.register(CustomHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CustomHeader.reuseIdentifier)
collectionView.backgroundColor = .white
self.view.addSubview(collectionView)
}
// UICollectionViewDataSource 必要方法实现
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return columnWidths.count * numberOfRows // 根据你的需要设置项数
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = colors.randomElement() // 简单地交替颜色以便区分单元格
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
guard kind == UICollectionView.elementKindSectionHeader else {
return UICollectionReusableView()
}
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CustomHeader.reuseIdentifier, for: indexPath) as! CustomHeader
header.configure(with: "Section \(indexPath.section)")
return header
}
// UICollectionViewDelegate 方法,根据需要实现
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// 单元格被点击时的操作
print("Selected cell at \(indexPath)")
// changeLayout()
}
func changeLayout() {
// 创建一个新的布局实例
let newLayout = CustomFlowLayout()
newLayout.columnWidths = [100, 100, 200, 100, 100] // 设置新的列宽
newLayout.numberOfRows = numberOfRows
newLayout.itemHeight = 50
columnWidths = newLayout.columnWidths
// 无动画地更新布局
collectionView.setCollectionViewLayout(newLayout, animated: false)
// 如果你希望有动画效果,可以设置 animated 为 true
// collectionView.setCollectionViewLayout(newLayout, animated: true)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let layout = collectionView.collectionViewLayout as? CustomFlowLayout else { return }
if scrollView.contentOffset.x != layout.stickyHeaderXOffset {
layout.invalidateLayout()
}
}
}
class CustomFlowLayout: UICollectionViewLayout {
private var cache = [UICollectionViewLayoutAttributes]()
private var headersCache = [Int: UICollectionViewLayoutAttributes]() // Header cache
var stickyHeaderXOffset: CGFloat = 0 // 用于保存固定 header 的 X 偏移量
var columnWidths: [CGFloat] = [] // 每列的宽度
var numberOfRows = 20
var itemHeight: CGFloat = 50
var headerWidth: CGFloat = 50 // Header 的宽度
var totalHeight: CGFloat {
itemHeight * CGFloat(numberOfRows)
}
override var collectionViewContentSize: CGSize {
let width = columnWidths.reduce(0, +) // 所有列宽度之和
let height = totalHeight // 总高度不包括 header
return CGSize(width: width + headerWidth, height: height)
}
override func prepare() {
guard let collectionView = collectionView else { return }
// cache.removeAll()
// headersCache.removeAll()
// 计算 header 的位置
stickyHeaderXOffset = collectionView.contentOffset.x
// 其他 cell 的布局计算
// 确保不重复添加 header 的布局属性
if headersCache.isEmpty {
let headerAttributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: IndexPath(item: 0, section: 0))
headerAttributes.frame = CGRect(x: stickyHeaderXOffset, y: 0, width: headerWidth, height: totalHeight)
headerAttributes.zIndex = 1024
headersCache[0] = headerAttributes
} else {
// 只更新 X 位置
if let headerAttributes = headersCache[0] {
headerAttributes.frame.origin.x = stickyHeaderXOffset
}
}
// var xOffset: CGFloat = headerWidth + collectionView.contentOffset.x // 开始布局 cell 的 x 偏移量
var yOffset: CGFloat = 0 // y 偏移量
guard cache.isEmpty else { return }
for item in 0..<collectionView.numberOfItems(inSection: 0) {
let column = item % columnWidths.count
let indexPath = IndexPath(item: item, section: 0)
let xOffset = columnWidths[0..<column].reduce(headerWidth, +) + collectionView.contentOffset.x
let frame = CGRect(x: xOffset, y: yOffset, width: columnWidths[column], height: itemHeight)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
cache.append(attributes)
yOffset += (item + 1) % columnWidths.count == 0 ? itemHeight : 0
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Add header attributes if in the rect
if let headerAttributes = headersCache[0], rect.intersects(headerAttributes.frame) {
visibleLayoutAttributes.append(headerAttributes)
}
// Add cell attributes if they intersect with the rect
visibleLayoutAttributes.append(contentsOf: cache.filter { attributes in
rect.intersects(attributes.frame)
})
return visibleLayoutAttributes
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return headersCache[indexPath.section]
}
}
class CustomHeader: UICollectionReusableView {
static let reuseIdentifier = "CustomHeader"
private let titleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .black
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
private func setupViews() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
addSubview(titleLabel)
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
backgroundColor = .lightGray // Set the background color for the header
}
func configure(with title: String) {
titleLabel.text = title
}
}
可以左右滑动的collectionView
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 这里就是对UICollectionView的一个个人总结,不喜勿喷,如有不妥之处,望请指正🙏🙏🙏 1.简介 UIC...
- UICollectionViewFlowLayout类是一个具体的布局对象,它将一个个部件组织成一个可分组的(每个...
- CollectioniOS聚合了项目搭建的一些基本模块,节约开发者时间,协助项目的快速搭建,能够满足一个项目的基本...
- iOS流布局UICollectionView系列一——初识与简单使用UICollectionView 一、简介 U...