Reputation: 634
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)
Upvotes: 10
Views: 11642
Reputation: 9935
As outlined in the other answers, we only really need the currently focused items as part of delegate methods like collectionView.shouldUpdateFocusIn
and collectionView.didUpdateFocusIn
for layout purposes. I would advise using these methods if we need to customise the UI depending on focus for collection and table views.
Use UIFocusItem.didUpdateFocus
, UIFocusItem.shouldUpdateFocus
outside of collection or table views. These methods are called for any view that is in the view hierarchy as the collection or table view being focused next or focused previously.
override func didUpdateFocus(
in context: UIFocusUpdateContext,
with coordinator: UIFocusAnimationCoordinator
) { }
override func shouldUpdateFocus(in context: UIFocusUpdateContext) -> Bool { }
If we're using the LLDB debugger:
$ po UIFocusDebugger.status()
returns the currently focused item.
More useful focus debugging tools can be found here. Outside of focused or unfocused UI state styling, UIFocusDebugger
is really handy.
UIScreen.main.focusedView
and UIScreen.main.focusedItem
are now deprecated, so I don't advise using these since they may be removed from the API soon. Apple prefers to use the delegate methods for managing focus updates.
Upvotes: 0
Reputation: 23996
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)")
}
}
Upvotes: 20
Reputation: 41
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:
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...
Upvotes: 4
Reputation: 32247
Here's how I accomplished this in shouldUpdateFocusInContext
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
}
Upvotes: 2
Reputation: 2169
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.
Upvotes: 9