【问题标题】:UICollectionView Horizontal Scrolling With Static Focused ItemUICollectionView 水平滚动与静态焦点项目
【发布时间】:2018-06-28 05:02:02
【问题描述】:

我正在尝试在 tvOS 上的 UITableViewCell 中创建一个可水平滚动的 UICollectionView,并且已经实现了我的目标。

接下来我需要做的是能够将焦点转移到 UICollectionView 中的下一个项目,但是整个项目堆栈应该向左或向右移动一个位置,但焦点将保持在视觉上的“第一个”项目。

这是我希望实现的一个小的 ASCII 图表:

Scenario 1, before focus shifts
     ---------------------
1.   | v   v   v   v   v |
2.   |[v]  v   v   v   v |
3.   | v   v   v   v   v |
     ---------------------

Scenario 1, after focus shifts
     ---------------------
1.   | v   v   v   v   v |
2.   | v  [v]  v   v   v |
3.   | v   v   v   v   v |
     ---------------------

上述场景 1 是默认行为,焦点转移到 UICollectionView 中的下一项。

Scenario 2, before focus shifts
     ---------------------
1.   | v   v   v   v   v |
2.   |[v]  v   v   v   v |
3.   | v   v   v   v   v |
     ---------------------

Scenario 2, after focus shifts
     ---------------------
1.   | v   v   v   v   v |
2. v |[v]  v   v   v     |
3.   | v   v   v   v   v |
     ---------------------

场景2中,我在遥控器上滑动选择UICollectionView中的下一个项目后,焦点应该保持在左侧,但焦点下方的项目应该移动一个。

有人对如何实现这一点有任何提示吗?

我在 UICollectionView 中阅读了关于粘性列的帖子,但我认为这不完全是粘性列?

【问题讨论】:

    标签: swift uicollectionview uikit tvos apple-tv


    【解决方案1】:

    我最近不得不为一个项目这样做。这是我使用的:

     override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
        if let focusView = context.nextFocusedView as? UICollectionViewCell, let indexPath = collection.indexPath(for: focusView) {
    
            collection.isScrollEnabled = false
            coordinator.addCoordinatedAnimations({
                self.collection.scrollToItem(at: indexPath, at: .left, animated: true)
            }, completion: nil)
        }
    }
    

    【讨论】:

    • 不错!不过有一个问题:content 做什么,第三个let 条件?还有,为什么要把isScrollEnabled设置成false
    • 抱歉,这是我在 viewController 中的数据源。除非您有可选数据,否则您不需要它。我将其设置为错误,因为用户可能会尝试在动画转换中间滚动到不同的索引并让 collectionView 奇怪地滚动。您可以将其设置为 false 以查看是否有相同的结果。
    【解决方案2】:

    从 tvOS 13 开始,您可以使用 UICollectionViewCompositionalLayout 的 UICollectionLayoutSectionOrthogonalScrollingBehavior(.groupPagin) 来处理水平滚动的静态焦点。 只需在组中添加一项!

    private func createCollectionViewLayout() -> UICollectionViewLayout {
            
        let sectionProvider = {
            (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
                let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
                let item = NSCollectionLayoutItem(layoutSize: itemSize)
                let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .absolute(100))
                let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
                let section = NSCollectionLayoutSection(group: group)
    
                section.orthogonalScrollingBehavior = .groupPaging
    
                return section
            }
        return UICollectionViewCompositionalLayout(sectionProvider: sectionProvider)
    }
    

    【讨论】:

      【解决方案3】:

      UICollectionViewDelegate 方法在焦点移动期间被调用。因此,要解决您的任务,您需要操作 UICollectionView 的 (UIScrollView) 内容偏移量。要实现类似“本机”的方式,您需要:

      实现UICollectionViewDelegate方法collectionView:didUpdateFocusInContext:withAnimationCoordiantor,保存下一个焦点单元格origin (x,y),并在UIScrollViewDelegate方法scrollViewWillEndDragging:withVelocity:targetContentOffset:中设置新的目标内容偏移量,指向焦点单元格起源。

      @interface CLViewController () <UICollectionViewDelegate, UICollectionViewDataSource>
      @property (nonatomic) CGPoint focusedCellPosition;
      @end
      
      @implementation CLViewController
      - (void)collectionView:(UICollectionView *)collectionView didUpdateFocusInContext:(UICollectionViewFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
          NSIndexPath *next = context.nextFocusedIndexPath;
          self.focusedCellPosition = [collectionView cellForItemAtIndexPath:next].frame.origin;
      }
      
      - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
          *targetContentOffset = self.focusedCellPosition;
      }
      @end
      

      【讨论】: