Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

tvOS UICollectionView get currently focused item's index path

So this is kind of a noobish question but I just can't figure out a very simple way to detect currently focused item's indexPath.
I looked around hoping to see something very easy like collectionView.indexPathOfCurrentlyFocusedItem but didn't find anything remotely close. So I digged around and tried to find something similar at UIFocusEnvironment, UIFocusUpdateContext trying to find the desired property but failed. So, the only solution I can come up with is just iterating through all visible cells and finding a cell with focused property set to true.

So is there a more simple and elegant way to find the currently focused item's indexPath? (Except tracking it through delegate method and saving it in view controller's property)

like image 753
xinatanil Avatar asked Sep 26 '15 20:09

xinatanil


People also ask

What is a uicollectionviewlayout class?

A subclass of the UICollectionViewLayout class, the layout object defines the organization and location of all cells and supplementary views inside the collection view. Although it defines their locations, the layout object doesn’t actually apply that information to the corresponding views.

How does the collection view apply layout information to corresponding views?

The collection view applies layout information to the corresponding views because the creation of cells and supplementary views involves coordination between the collection view and your data source object. The layout object is like another data source, except it provides visual information instead of item data.

How does uicollectionviewdiffabledatasource work?

A UICollectionViewDiffableDataSource object manages this process automatically. If you're using a custom data source, then whenever you add, delete, or rearrange data in your collection, you use the methods of UICollectionView to insert, delete, and rearrange the corresponding cells.

How do I rearrange items in a uicollectionviewcontroller?

The UICollectionViewController class provides a default gesture recognizer that you can use to rearrange items in its managed collection view. To install this gesture recognizer, set the installsStandardGestureForInteractiveMovement property of the collection view controller to true.


Video Answer


4 Answers

You can use UIScreen property focusedView as followed for this:

if let focusedCell = UIScreen.main.focusedView as? UICollectionViewCell {
    if let indexPath = collectionView.indexPath(for: focusedCell) {
        print("IndexPath is \(indexPath)")
    }
}
like image 125
Antoine Avatar answered Sep 18 '22 14:09

Antoine


So, your target is to do something when you get and lose focus particularly on a cell under tvOS. The catch is you're moving around other UI elements, and therefore the context could be different. You have to change in this context only those UIs that you have to care of.

The right place to make your implementation is func didUpdateFocusInContext(), like this:

override func didUpdateFocusInContext(
  context:                              UIFocusUpdateContext,
  withAnimationCoordinator coordinator: UIFocusAnimationCoordinator
) {
  coordinator.addCoordinatedAnimations({
    if let cell = context.previouslyFocusedView as? UICollectionViewCell {
      cell.layer.borderWidth = 2
    }

    if let cell = context.nextFocusedView as? UICollectionViewCell {
      cell.layer.borderWidth = 5
    }
  },
  completion: nil)
}

Now we're using the focus coordinator to apply our logic:

  • When the previously focused item is UICollectionViewCell then you have to release the focus to the next item. You shouldn't care what is the next item because it could be a collection cell or not. For fun, in this case, let's change the border to 2. This value could be set by default.
  • When the next focused item is UICollectionViewCell then you've to handle it is a similar way, or it will become a mess... So, let's change the border to 5.

As you can see, didUpdateFocusInContext() provides a generic approach for all views within your current visual context. You can apply the same approach for other UI elements.

Have a fun with tvOS...

like image 21
Andrew Andreev Avatar answered Sep 22 '22 14:09

Andrew Andreev


Use didUpdateFocusInContect - UICollectionViewDelegate

func collectionView(collectionView: UICollectionView, didUpdateFocusInContext context: UICollectionViewFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
    if collectionView == self.collectionView {
        print(context.nextFocusedIndexPath)
    }
}

This wil return the indexPath of the cell that is going to be focused, you could also try:

context.previouslyFocusedIndexPath

Depends what you're trying to do.

like image 30
Jeroen Bakker Avatar answered Sep 19 '22 14:09

Jeroen Bakker


Here's how I accomplished this in shouldUpdateFocusInContext

Solution

override func shouldUpdateFocusInContext(context: UIFocusUpdateContext) -> Bool {

    // The magic is in the next two lines
    let cell: UICollectionViewCell = context.nextFocusedView as! UICollectionViewCell
    let indexPath: NSIndexPath? = self.collectionView.indexPathForCell(cell)

    print(indexPath) 
    // <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}
    
    return true
}
like image 33
Jacksonkr Avatar answered Sep 19 '22 14:09

Jacksonkr