美文网首页
可以左右滑动的collectionView

可以左右滑动的collectionView

作者: 焦下客 | 来源:发表于2024-04-30 19:28 被阅读0次
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

      本文链接:https://www.haomeiwen.com/subject/rqzxfjtx.html