将 collectionview 中的活动单元格保存在一个地方

Keep active cell in collectionview in one place

我正在开发一个 tvOS 应用程序,我希望 collectionView 的活动单元格始终位于同一位置。换句话说,我不希望活动单元格通过我的 collectionView,但我希望 collectionView 通过我的活动单元格。

下图中的示例。活动标签始终位于屏幕中央,而您可以滚动 collectionView 以获得不同的活动标签。

基本上是一个自定义 UI 的 pickerview。但遗憾的是tvOS上没有提供iOS的pickerview...

我已经调查过 UICollectionViewScrollPosition.verticallyCentered。这部分让我到达那里,但这还不够。如果我滚动得非常快,活动项目会跳到列表的下方,当我暂停时,它会一直向上滚动到中心。我真的希望活动项目始终位于屏幕中央。

关于如何做到这一点有什么想法吗?


根据@HMHero 回答更新

好的,我试着按照你告诉我的去做,但无法使滚动正常工作。这可能是由于我计算了偏移量,或者因为(就像你说的)setContentOffset(, animated: ) 不起作用。

现在我做了以下操作,但不确定从这里到哪里去;

禁用滚动并将最后一个和第一个标签居中

override func viewDidLoad() {
    super.viewDidLoad()
    //Disable scrolling
    self.collectionView.isScrollEnabled = false
    //Place the first and last label in the middle of the screen
    self.countryCollectionView.contentInset.top = collectionView.frame.height * 0.5 - 45
    self.countryCollectionView.contentInset.bottom = collectionView.frame.height * 0.5 - 45
}

获取标签的位置以检索距屏幕中心的距离(偏移量)

func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
if let indexPath = context.nextFocusedIndexPath,
        let cell = countryCollectionView.cellForItem(at: indexPath) {
            //Get the center of the next focused cell, and convert that to position in the visible part of the (tv) screen itself
            let cellCenter = countryCollectionView.convert(cell.center, to: countryCollectionView.superview)
            //Get the center of the collectionView
            let centerView = countryCollectionView.frame.midY
            //Calculate how far the next focused cell y position is from the centerview. (Offset)
            let offset = cellCenter.y - centerView
    }
}

打印时偏移量returns 增量为100。标签的高度为 90,间距为 10。所以我认为这是正确的,尽管它一直运行到 2400(最后一个标签)。

关于从这里到哪里去的任何想法?

我在 tvOS 上工作已经一年多了,但我记得它应该相当简单,只需一个简单的技巧。使用更新后的 api 可能有更好的方法来执行此操作,但这就是我所做的。

1) 禁止滚动集合视图 isScrollEnabled = false

2) In delegate, optional public func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator), get nextFocusedIndexPath 并计算单元格的偏移量在集合视图。

3) 使用偏移量,在委托回调中手动设置滚动动画。

我找到了旧的 post 我有了一个想法。

How to center UICollectionViewCell that was focused?

我最终做的与线程中的答案有些不同,但这是一个很好的提示。

好的,加上@HMHero的一些指点,达到了预期的效果。它涉及使用自定义动画对 contentOffset 进行动画处理。这是我的代码;

func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {

    if let previousIndexPath = context.previouslyFocusedIndexPath,
        let cell = countryCollectionView.cellForItem(at: previousIndexPath) {
        UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
            cell.contentView.alpha = 0.3
            cell.contentView.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
        })
    }

    if let indexPath = context.nextFocusedIndexPath,
        let cell = countryCollectionView.cellForItem(at: indexPath) {
        let cellCenter = CGPoint(x: cell.bounds.origin.x + cell.bounds.size.width / 2, y: cell.bounds.origin.y + cell.bounds.size.height / 2)
        let cellLocation = cell.convert(cellCenter, to: self.countryCollectionView)
        let centerView = countryCollectionView.frame.midY
        let contentYOffset = cellLocation.y - centerView

        UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
            collectionView.contentOffset.y = contentYOffset
            cell.contentView.alpha = 1.0
            cell.contentView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
            self.countryCollectionView.layoutIfNeeded()
        })

    }
}