UICollectionView 水平滚动静态焦点项目

UICollectionView Horizontal Scrolling With Static Focused Item

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

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

这是我希望实现的 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 中,当我在遥控器上滑动到 select UICollectionView 中的下一个项目后,焦点应该留在左边,但下面的项目焦点应该移动一个。

有没有人对如何实现这一目标有任何提示?

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

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

 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)
    }
}

从 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)
}

UICollectionViewDelegate 方法在焦点移动期间正在调用。所以要解决你的任务你需要操作UICollectionView(UIScrollView)的内容偏移量。要实现类似 'native' 的方式,您需要:

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

@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