Reputation: 5019
I am building a tvos
application. i have a strange bug where UICollectionView
lose focus of the previously selected cell when i navigate back to that particular view. The scenario is some thing this like this.
I have two UIViewControllers
A
and B
. A
has a UITableView
and it has three prototype cells in it. Each cell has a horizontal scrolling UICollectionView
inside it. When i click on any of UICollectionViewCell
it navigates to the B
(detail page). I am presenting B
modally.
Now when i press Menu button on Siri remote view A
appears again (in other words view B
is removed from View hierarchy) but the current selected cell is different then the previously selected. I have tried to use remembersLastFocusedIndexPath
with both true
and false
values and also tried by implementing
func indexPathForPreferredFocusedViewInCollectionView(collectionView: UICollectionView) -> NSIndexPath?
but the control neves comes to this function when i navigate back to view A. I am also reloading every thing in viewWillAppear
function.
Can any one help me in this. Thanks
Upvotes: 3
Views: 5058
Reputation: 564
In Swift
If you want to focus collection view Cell then You can use collectionview Delegate method, method name is
func collectionView(collectionView: UICollectionView, didUpdateFocusInContext context: UICollectionViewFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
}
You can use this method like this...
func collectionView(collectionView: UICollectionView, didUpdateFocusInContext context: UICollectionViewFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
if let previousIndexPath = context.previouslyFocusedIndexPath,
let cell = collectionView.cellForItemAtIndexPath(previousIndexPath) {
cell.contentView.layer.borderWidth = 0.0
cell.contentView.layer.shadowRadius = 0.0
cell.contentView.layer.shadowOpacity = 0
}
if let indexPath = context.nextFocusedIndexPath,
let cell = collectionView.cellForItemAtIndexPath(indexPath) {
cell.contentView.layer.borderWidth = 8.0
cell.contentView.layer.borderColor = UIColor.blackColor().CGColor
cell.contentView.layer.shadowColor = UIColor.blackColor().CGColor
cell.contentView.layer.shadowRadius = 10.0
cell.contentView.layer.shadowOpacity = 0.9
cell.contentView.layer.shadowOffset = CGSize(width: 0, height: 0)
collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: [.CenteredHorizontally, .CenteredVertically], animated: true)
}
}
Upvotes: 0
Reputation: 361
The property remembersLastFocusedIndexPath should be set to true for the collectionView and false for the tableView.
Also, Are you reloading the UITableView in viewWillAppear i.e Is the table data being refreshed when the B is popped and A appears? If Yes, then lastFocusedIndexPath will be nil on reload.
We faced the same issue. We solved it by not reloading the contents when B is popped.
Maintain a flag say didPush. Set this flag to true when B is pushed. When A appears check whether the flag is set and only then fetch data and reload table.
This worked for us.
Upvotes: 3
Reputation: 197
I don't remember exactly, but I know there was a known issue for remembersLastFocusedIndexPath
where it wasn't working as intended.
This is one workaround, although take it with a grand of salt as it does seem slightly hacky and it uses the common (but potentially unstable) approach of overriding the preferredFocusedView
property.
private var viewToFocus: UIView?
override var preferredFocusView: UIView? {
get {
return self.viewToFocus
}
}
Save locally the indexPath of the last cell in View A
when presenting View B
// [1] Saving and scrolling to the correct indexPath:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
...
collectionView.scrollToItemAtIndexPath(indexPath:, atScrollPosition:, animated:)
}
// [2] Create a dispatchTime using GCD before setting the cell at indexPath as the preferredFocusView:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(CGFloat.min * CGFloat(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.viewToFocus = collectionView.cellForItemAtIndexPath(indexPath:)
// [3] Request an update
self.setNeedsFocusUpdate()
// [4] Force a focus update
self.updateFocusIfNeeded()
}
}
The reason we split the two methods into both viewWillAppear
and viewDidAppear
is that it eliminates a bit of the animation jump. If anyone else could jump in with suggestions to improve or even alternate solutions, I'd also be interested!
Upvotes: 1