Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

indexPathForPreferredFocusedView is not being Called

I need to specify which cell should receive focus.

According to Apple Documentation the indexPathForPreferredFocusedView delegate method should be called if the remembersLastFocusedIndexPath property is false, or if there is no saved index path because no cell was previously focused.

In my case I am using a collection view in a UIViewController and setting remembersLastFocusedIndexPath to false but indexPathForPreferredFocusedView is not being called.

How explain this behaviour?

like image 460
Aymen HARRATH Avatar asked Feb 02 '18 15:02

Aymen HARRATH


3 Answers

The function indexPathForPreferredFocusedView is part of UICollectionViewDelegate, so it might be that the delegate was not assigned to your collectionView.

Or, the problem might also be on the focus environment, not taking into account your UICollectionView.

As a reference, here you have an example of a simple collectionView with 5 cells, having the one in the center initially selected by default

import UIKit

class ViewController: UIViewController {

    private var collectionView: UICollectionView!
    private var items = ["One", "Two", "Three", "Four", "Five"]

    override var preferredFocusEnvironments: [UIFocusEnvironment] {
        return [collectionView]
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UICollectionViewFlowLayout())
        collectionView.remembersLastFocusedIndexPath = false
        MyCell.register(in: collectionView)
        view.addSubview(collectionView)

        collectionView.dataSource = self
        collectionView.delegate = self
    }
}

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCell.reuseIdentifier, for: indexPath) as! MyCell
        cell.titleLabel.text = items[indexPath.row]
        return cell
    }
}

extension ViewController: UICollectionViewDelegate {

    func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath? {
            return IndexPath(row: 2, section: 0)
    }
}

class MyCell: UICollectionViewCell {

    static var reuseIdentifier: String { return String(describing: self) + "ReuseIdentifier" }

    var titleLabel: UILabel!

    public static func register(in collectionView: UICollectionView) {
        collectionView.register(MyCell.self, forCellWithReuseIdentifier: MyCell.reuseIdentifier)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        titleLabel = UILabel(frame: bounds)
        backgroundColor = .blue
        contentView.addSubview(titleLabel)
    }

    override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
        backgroundColor = isFocused ? .red : .blue
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

collectionView

like image 121
David Cordero Avatar answered Oct 10 '22 13:10

David Cordero


You should set remembersLastFocusedIndexPath to true.

collectionView.remembersLastFocusedIndexPath = true
like image 29
Marouane Lanouari Avatar answered Oct 10 '22 13:10

Marouane Lanouari


Swift 4: I had the same issue. I wanted to reload collectionview and force focus on a particluar cell. If I set collectionView.remembersLastFocusedIndexPath = true, indexPathForPreferredFocusedView delegate method is called. But it will remember the last focused indexPath as the property name says. This is not what I wanted. Apple docs are confusing.

Try this:

  1. Implement the delegate method:

    func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath? {
    return IndexPath(item: 5, section: 0)
    }
    
  2. Force update the focus [ex: in a button action]:

    collectionView.setNeedsFocusUpdate()
    collectionView.updateFocusIfNeeded()
    

This will force the collectionView to call the indexPathForPreferredFocusedView method. We don't have to set the remembersLastFocusedIndexPath to true.

NB: I had multiple collectionViews in my screen and the above steps will work only if the currently focused collectionView and the collectionView which we are forcing the focus update are same.

Use the preferredFocusEnvironments to return the preferred collectionview to be in focus when the view loads [for example].

override var preferredFocusEnvironments: [UIFocusEnvironment] {
    return [collectionView2]
}
like image 3
abhimuralidharan Avatar answered Oct 10 '22 13:10

abhimuralidharan