确定单元格是否已从集合视图 iOS 中的可见区域滚动

Determine if a cell has scrolled off the visible area in collection view iOS

提问人:iCodes 提问时间:9/6/2023 更新时间:9/6/2023 访问量:21

问:

我有一个集合视图,其中我有一个特定的单元格。如果此单元格在向上滚动时从底部的可见区域滚动,则必须显示视图。我们怎样才能做到这一点?

为了识别这个特定的单元格,我存储了它的 indexPath。现在,如果我们使用下面的检查,即使单元格在向下移动时滚动过去,即单元格已从屏幕顶部滚动,它也是有效的。但是我希望实现的是,当用户向上滚动时,只有当单元格从屏幕底部滚动时,才能找出答案。!collectionView.indexPathsForVisibleItems.contains(specificCellIndexPath)

func scrollViewDidScroll(_ scrollView: UISCrollView) {

    if !collectionView.indexPathsForVisibleItems.contains(specificCellIndexPath) {
       // show the desired view only when specificCellIndexPath has scrolled past the bottom of the screen.
    }
}
iOS UI卷轴视图

评论

0赞 Maziar Saadatfar 9/6/2023
这个功能帮不了你吗?func collectionView(_ collectionView: UICollectionView, willDisplay 单元格: UICollectionViewCell, forItemAt indexPath: IndexPath) { }
0赞 iCodes 9/6/2023
使用此委托函数的问题在于,当单元格从屏幕顶部和底部滚动时,它将被调用。但是,我只寻找从底部滚动。
0赞 Maziar Saadatfar 9/6/2023
您可以拥有页面的顶部和底部索引,并显示下一个和上一个索引,您可以查看单元格的高度和集合视图高度,您可以计算滚动的单元格。
0赞 Rajesh Vekariya 9/6/2023
您可以结合使用 scrollViewDidScroll(:) 和 collectionView(:willDisplay:forItemAt:) 委托方法
0赞 Maziar Saadatfar 9/6/2023
例如,您可以了解集合视图的最后一项何时显示: func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if indexpath.item == (yourList.count - 1) { print(“the last index”) }}

答:

0赞 DonMag 9/6/2023 #1

您的目标似乎是想知道:

  • specificPath单元格位于集合视图框架下方
  • specificPath单元格可见
  • specificPath单元格位于集合视图框架上方

因此,我们将添加一个类属性来查找 specificPath 单元格的 Bottom:

let specificPath: IndexPath = .init(item: 10, section: 0)

// we will update when the specificPath cell is visible
var specificCellBottom: CGFloat = .greatestFiniteMagnitude
    

在 中,我们可以找出单元格是否可见......如果不是,请确定它是在集合视图框架的上方还是下方:scrollViewDidScroll()

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    
    var cellPosition: Int = 1

    if collectionView.indexPathsForVisibleItems.contains(specificPath) {

        // cell is Visible
        cellPosition = 0

        // it's visible, so we know we can get it, but safely unwrap optional
        if let c = collectionView.cellForItem(at: specificPath) {
            // this will not change after the cell has been shown,
            //  but no harm in setting it every time
            specificCellBottom = c.frame.maxY
        }

    } else {

        if scrollView.contentOffset.y >= specificCellBottom {
            cellPosition = -1
        }

    }
    
    switch cellPosition {
    case 1:
        // specificPath cell is Below the Collection View
    case 0:
        // specificPath cell is Visible
    default:
        // specificPath cell is Above the Collection View
    }
}

这里有一个完整的例子可以玩......

简单居中的标签单元格:

class CenterLabelCell: UICollectionViewCell {
    
    let label = UILabel()
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        label.backgroundColor = .yellow
        label.font = .systemFont(ofSize: 16.0, weight: .light)
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(label)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: g.centerYAnchor),
        ])
    }
}

视图控制器示例:

class VisibleCellCollViewVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
    
    var collectionView: UICollectionView!
    let statusLabel = UILabel()
    
    let specificPath: IndexPath = .init(item: 10, section: 0)
    
    // we will update when the specificPath cell is visible
    var specificCellBottom: CGFloat = .greatestFiniteMagnitude
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = 12.0
        layout.scrollDirection = .vertical
    
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)

        // we'll put the label below the collection view
        statusLabel.textAlignment = .center
        statusLabel.text = "\(specificPath) cell is Below the Collection View"
        statusLabel.backgroundColor = .cyan
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)

        statusLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(statusLabel)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            statusLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            statusLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            statusLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            statusLabel.heightAnchor.constraint(equalToConstant: 60.0),

            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            collectionView.bottomAnchor.constraint(equalTo: statusLabel.topAnchor, constant: -20.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            
        ])
        
        collectionView.register(CenterLabelCell.self, forCellWithReuseIdentifier: "c")
        collectionView.dataSource = self
        collectionView.delegate = self
        
        collectionView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return .init(width: collectionView.frame.width * 0.5 - 8.0, height: 200.0)
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 50
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "c", for: indexPath) as! CenterLabelCell
        cell.label.text = "\(indexPath)"
        cell.contentView.backgroundColor = .systemBlue
        if indexPath == specificPath {
            cell.contentView.backgroundColor = .systemRed
        }
        return cell
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        var cellPosition: Int = 1

        if collectionView.indexPathsForVisibleItems.contains(specificPath) {

            // cell is Visible
            cellPosition = 0

            // it's visible, so we know we can get it, but safely unwrap optional
            if let c = collectionView.cellForItem(at: specificPath) {
                // this will not change after the cell has been shown,
                //  but no harm in setting it every time
                specificCellBottom = c.frame.maxY
            }

        } else {

            if scrollView.contentOffset.y >= specificCellBottom {
                cellPosition = -1
            }

        }
        
        switch cellPosition {
        case 1:
            statusLabel.text = "\(specificPath) cell is Below the Collection View"
        case 0:
            statusLabel.text = "\(specificPath) cell is Visible"
        default:
            statusLabel.text = "\(specificPath) cell is Above the Collection View"
        }
    }
    
}

看起来像这样:

enter image description here

enter image description here

enter image description here