如何检测最后一个单元格是不是在 UICollectionView 中可见?

Posted

技术标签:

【中文标题】如何检测最后一个单元格是不是在 UICollectionView 中可见?【英文标题】:How to detect if last cell is visible in UICollectionView?如何检测最后一个单元格是否在 UICollectionView 中可见? 【发布时间】:2016-06-18 14:44:59 【问题描述】:

我正在尝试检测collectionView 中的最后一个单元格是否可见。

var isLastCellVisible: Bool 

    let lastIndexPath = NSIndexPath(forItem: self.messages.count - 1, inSection: 0)
    let visibleIndexPaths = self.collectionView.indexPathsForVisibleItems()

    let lastCellPositionY = self.collectionView.layoutAttributesForItemAtIndexPath(lastIndexPath)!.frame.origin.y
    let bottomInset = self.collectionView.contentInset.bottom // changes when the keyboard is shown / hidden
    let contentHeight = self.collectionView.contentSize.height

    if visibleIndexPaths.contains(lastIndexPath) && (contentHeight - lastCellPositionY) > bottomInset 
        return true
     else 
        return false
    

什么有效:

如果最后一个单元格可见并且显示键盘以使该单元格没有被它隐藏,则上面的代码返回 true。如果它隐藏,则返回 false。

但是当用户向上滚动并且最后一个单元格位于键盘上方时,我不知道如何返回 true。

lastCellPositionYcontentHeightcollectionView 向上滚动时不会更改。 反而 self.collectionView.bounds.origin.y 确实发生了变化,但我不知道如何将 lastCellPositionY 与它进行比较,因为它们的来源不同,而且 self.collectionView.bounds.origin.y 明显小于 lastCellPositionY

【问题讨论】:

【参考方案1】:

我在用什么

override func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) 
        if indexPath.row == dataSource.count - 1 
            // Last cell is visible
        
    

【讨论】:

【参考方案2】:

我使用这段代码(从这个线程中的提交翻译成 Swift:https://github.com/jessesquires/JSQMessagesViewController/issues/1458)

var isLastCellVisible: Bool 

    if self.messages.isEmpty 
        return true
    

    let lastIndexPath = NSIndexPath(forItem: self.messages.count - 1, inSection: 0)
    var cellFrame = self.collectionView.layoutAttributesForItemAtIndexPath(lastIndexPath)!.frame

    cellFrame.size.height = cellFrame.size.height

    var cellRect = self.collectionView.convertRect(cellFrame, toView: self.collectionView.superview)

    cellRect.origin.y = cellRect.origin.y - cellFrame.size.height - 100 
    // substract 100 to make the "visible" area of a cell bigger

    var visibleRect = CGRectMake(
        self.collectionView.bounds.origin.x,
        self.collectionView.bounds.origin.y,
        self.collectionView.bounds.size.width,
        self.collectionView.bounds.size.height - self.collectionView.contentInset.bottom
    )

    visibleRect = self.collectionView.convertRect(visibleRect, toView: self.collectionView.superview)

    if CGRectContainsRect(visibleRect, cellRect) 
        return true
    

    return false

【讨论】:

【参考方案3】:

一个方便的扩展,有两个计算属性。

extension UICollectionView 

    var isLastItemFullyVisible: Bool 

        let numberOfItems = numberOfItems(inSection: 0)
        let lastIndexPath = IndexPath(item: numberOfItems - 1, section: 0)

        guard let attrs = collectionViewLayout.layoutAttributesForItem(at: lastIndexPath) 
        else  
            return false 
        
        return bounds.contains(attrs.frame)
    

    // If you don't care if it's fully visible, as long as some of it is visible
    var isLastItemVisible: Bool 
       let numberOfItems = collectionView.numberOfItems(inSection: 0)
       return indexPathsForVisibleItems.contains(where:  $0.item == numberOfItems - 1 )
    

isLastItemFullyVisible 有一个替代方案。一个班轮。

var isLastItemFullyVisible: Bool 
   contentSize.width == contentOffset.x + frame.width - contentInset.right

【讨论】:

【参考方案4】:

对于 Swift 3:

var isLastCellVisible: Bool 

    if self.posts.isEmpty 
        return true
    

    let lastIndexPath = IndexPath(item: self.posts.count - 1, section: 1)
    var cellFrame = self.collectionView?.layoutAttributesForItem(at: lastIndexPath)!.frame

    cellFrame?.size.height = (cellFrame?.size.height)!

    var cellRect = self.collectionView?.convert(cellFrame!, to: self.collectionView?.superview)

    cellRect?.origin.y = (cellRect?.origin.y)! - (cellFrame?.size.height)! - 100 
    // substract 100 to make the "visible" area of a cell bigger

    var visibleRect = CGRect(x: (self.collectionView?.bounds.origin.x)!, y: (self.collectionView?.bounds.origin.y)!, width: 
        (self.collectionView?.bounds.size.width)!, height: 
        (self.collectionView?.bounds.size.height)! - (self.collectionView?.contentInset.bottom)!
    )

    visibleRect = (self.collectionView?.convert(visibleRect, to: self.collectionView?.superview))!

    if visibleRect.contains(cellRect!) 
        return true
    

    return false

【讨论】:

【参考方案5】:

斯威夫特 5

public extension IndexPath 

  func isLastRow(at tableView: UITableView) -> Bool 
    return row == (tableView.numberOfRows(inSection: section) - 1)
  

  func isLastRow(at collectionView: UICollectionView) -> Bool 
    return row == (collectionView.numberOfItems(inSection: section) - 1)
  

对于集合视图

public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) 
    if indexPath.isLastRow(at: collectionView) 
      ..//
    

对于表格视图

public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    if indexPath.isLastRow(at: collectionView) 
      ..//
    

【讨论】:

【参考方案6】:

Swift 4.2 版

 func scrollViewDidScroll(_ scrollView: UIScrollView) 

    if (scrollView.bounds.maxY) == scrollView.contentSize.height
        // Call Your API or hide UIElements
     
 

【讨论】:

以上是关于如何检测最后一个单元格是不是在 UICollectionView 中可见?的主要内容,如果未能解决你的问题,请参考以下文章

iOS UICollectionView 检测是不是有不可见的单元格

如何判断 CollectionViewCell 是不是位于屏幕中心

AQGridView长按网格单元格检测

如何在 iOS 的 UICollectionViewCell 的左上角添加按钮?

将 UIView 动态添加到 UICollectionView 中的单元格仅适用于第一个单元格

如何在 UICollectionView 中检测“仅单指”单元格上的双击?