请稍侯

uicollectionview

11 April 2023

UICollectionView实现一个无限循环列表

import UIKit
import SnapKit

class InfiniteScrollViewController: UIViewController {
    private let cellIdentifier = "MyCell"
    private let items = ["1", "2", "3", "4", "5"]
    private var collectionView: UICollectionView!
    private var currentIndex = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        // 创建UICollectionViewFlowLayout对象,并设置itemSize、minimumLineSpacing等属性
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        flowLayout.minimumLineSpacing = 0
        flowLayout.scrollDirection = .horizontal
        
        // 创建UICollectionView对象,并注册cell、设置数据源和代理
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collectionView.backgroundColor = .white
        collectionView.register(InfiniteUICollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier)
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.isPagingEnabled = true
        view.addSubview(collectionView)
        
        // 设置collectionView水平方向上的约束
        collectionView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        // 将collectionView的contentOffset调整到中间位置
        let middleIndex = Int(items.count / 2)
        let indexPath = IndexPath(item: middleIndex, section: 0)
        collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
        currentIndex = middleIndex
    }
    
    // 计算当前显示的索引
    private func calculateCurrentIndex() -> Int {
        let contentOffset = collectionView.contentOffset.x
        let index = Int(round(contentOffset / UIScreen.main.bounds.width))
        return index
    }
}

extension InfiniteScrollViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // 数据源中的项数设置为items数组的两倍
        return items.count * 2
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! InfiniteUICollectionViewCell
        let itemIndex = indexPath.item % items.count
        cell.update(itemIndex: itemIndex, text: items[itemIndex])
        return cell
    }
}

class InfiniteUICollectionViewCell: UICollectionViewCell {
    
    var label: UILabel = {
        return UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)

        contentView.addSubview(label)
        label.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func update(itemIndex: Int, text: String){
        contentView.backgroundColor = (itemIndex % 2 == 0) ? .red : .orange
        label.text = text
    }
}

extension InfiniteScrollViewController: UICollectionViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let index = calculateCurrentIndex()
        if currentIndex != index {
            currentIndex = index
            let middleIndex = items.count
            if index < middleIndex {
                // 向前滚动一整页
                let targetIndex = index + items.count
                collectionView.scrollToItem(at: IndexPath(item: targetIndex, section: 0), at: .centeredHorizontally, animated: false)
            } else if index > middleIndex {
                // 向后滚动一整页
                let targetIndex = index - items.count
                collectionView.scrollToItem(at: IndexPath(item: targetIndex, section: 0), at: .centeredHorizontally, animated: false)
            }
        }
    }
}