swiftuicollectionviewuikittvosapple-tv

UICollectionView Horizontal Scrolling With Static Focused Item


I'm trying to create a horizontally-scrollable UICollectionView embedded within a UITableViewCell on tvOS, and have achieved my goal.

What I need to do next, is to be able to have the focus shift to the next item in the UICollectionView, but the whole stack of items should shift one position to the left or right, but the focus will remain at the 'first' item visually.

Here's a little ASCII diagram of what I'm hoping to achieve:

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

The above, Scenario 1, is the default behaviour, where the focus shifts to the next item in the 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 |
     ---------------------

In Scenario 2, after I swipe on the remote to select the next item in the UICollectionView, the focus should remain on the left, but the item underneath the focus should shift by one.

Does anyone have any hints on how this can be achieved?

I have read posts on sticky columns in UICollectionView, but this is not exactly a sticky column, I think?


Solution

  • I had to do this for a project recently. Here is what I used:

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