在 UICollectionView 滚动后获取可见项目的更新 IndexPath

Posted

技术标签:

【中文标题】在 UICollectionView 滚动后获取可见项目的更新 IndexPath【英文标题】:Get updated IndexPath for visible item after UICollectionView has scrolled 【发布时间】:2017-12-09 11:16:50 【问题描述】:

我有一个不同时期(每年)的 collectionView 供用户选择。每个项目的大小等于 collectionView 本身的大小。

@IBOutlet weak var periodCollectionView: UICollectionView!
@IBOutlet weak var itemLabel: UILabel!

let collectionValues = ["Day", "Week", "Month", "Year"]
var currentVisibleItem: Int?

override func viewDidLoad() 
    super.viewDidLoad()



func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 4


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 

    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PeriodName", for: indexPath) as? DisplayPeriodCollectionViewCell
    else 
        fatalError("Unable to cast collection view cell as DisplayPeriodCollectionViewCell")
    

    cell.periodName.text = collectionValues[indexPath.item]

    return cell



func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    let height = collectionView.frame.height
    let width = collectionView.frame.width

    return CGSize(width: width, height: height)

我想要做的是获取一个信息标签,以在 collectionView 滚动后显示当前可见项目的索引。她是代码:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 

    let centerPoint = CGPoint(x: periodCollectionView.center.x + periodCollectionView.contentOffset.x, y: 1.0)
    print("centerPoint: \(centerPoint)")

    if let currentItemIndexPath = periodCollectionView.indexPathForItem(at: centerPoint) 
        currentVisibleItem = currentItemIndexPath.item
        print("index of current item: \(currentVisibleItem!)")
        itemLabel.text = "\(currentVisibleItem!)"
     else 
        print("could not get index of current item...")
    


我添加了打印语句来检查值是否在运行时正确定义,它们显示一切正常。问题是标签在滚动结束后并不总是更新其文本,而是等待新的滚动发生:

我不知道为什么会这样 - 在scrollViewDidEndDecelerating 设置断点表明所有代码都已执行,那么标签文本有什么问题?

UPD。未更新标签文本似乎是模拟器问题 - 在设备上运行时,标签会正确更新。

【问题讨论】:

scrollViewDidEndScrollingAnimation(_:) 工作得更好吗? 我不会仅仅依赖模拟器问题。这可能只是时间问题,您只是在设备上看不到它,但它可能发生在速度较慢/速度较快的设备上。 问题是每次集合视图滚动时都会执行所有代码 - 包括标签更新。我用断点检查了它。但在模拟器上,标签更新由于某种原因没有显示。 我已经看到了在之前滚动的滚动视图中更新标签的问题。我想知道滚动动画是否在模拟器上尚未完成,但在您尝试更新标签时已在设备上完成。你试过scrollViewDidEndScrollingAnimation吗? scrollViewDidEndScrollingAnimation 根本不起作用 - 无论是在控制台(打印语句)还是在模拟器中。 【参考方案1】:

试试这个

override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
    let currentIndex = scrollView.contentOffset.x / CGFloat((itemWidth + interitemSpacing / 2))
    print(currentIndex)

【讨论】:

这是一种更紧凑的获取索引的方法,感谢您的提示!关于标签文本更新 - 似乎是模拟器问题,请检查问题更新【参考方案2】:

请尝试以下代码。

我在实际项目中多次使用它,效果很好。

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) 
    
    let visibleIndex = Int(targetContentOffset.pointee.x / collectionView.frame.width)
    print(visibleIndex)


【讨论】:

【参考方案3】:

在您的情况下,您可以使用:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

因为每个项目的大小与您的集合视图相同。 如果您仍想使用滚动视图委托,您应该在以下委托函数中编写页面计算:

func scrollViewDidEndDragging(UIScrollView, willDecelerate: Bool)

func scrollViewDidEndDecelerating(UIScrollView)

func scrollViewDidEndScrollingAnimation(UIScrollView)

【讨论】:

cellForItemAt 方法的问题是它对于当前项目的 indexPath 非常不可预测。向前滚动时,它会被调用两次——针对即将到来的单元格和之后的单元格。向后滚动时,它可能根本不会被调用,因为该单元格仍在堆栈中。 你有没有尝试使用 func tableView(UITableView, willDisplay: UITableViewCell, forRowAt: IndexPath)? 实现所有三个滚动视图委托方法确实有助于为 gif 上的箭头按钮提供动力。他们应该遍历集合项,但是单个 scrollViewDidEndDecelerating 方法对他们不起作用。 willDisplay 给出了相同的不可预测的结果——这意味着它可能返回的不是当前单元格,而是它之后或之前的单元格,具体取决于滚动方向。对于当前指数的精确计算,最好的方法是在下面的答案中提供@DenisLitvin。

以上是关于在 UICollectionView 滚动后获取可见项目的更新 IndexPath的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView cellForItemAtIndexPath 滚动后返回 nil

使 UICollectionView 可滚动,即使它未满

在 scrollToItemAtIndexPath 之后获取 UICollectionView 单元格位置

uicollectionview 可重用单元格图像在滚动时加载

iOS 使用 PullToRefreshView 使 UICollectionView 可滚动

如何让 UICollectionView 反弹来说明可滚动性?