indexPathForPreferredFocusedView не вызывается

Мне нужно указать, какая ячейка должна получить фокус.

Согласно документации Apple, indexPathForPreferredFocusedView метод делегата должен быть вызван, если remembersLastFocusedIndexPath свойство falseили, если нет сохраненного пути индекса, потому что ни одна ячейка не была ранее сфокусирована.

В моем случае я использую представление коллекции в UIViewController и настройка remembersLastFocusedIndexPath в false но indexPathForPreferredFocusedView не вызывается.

Как объяснить это поведение?

4 ответа

Вы должны установить remembersLastFocusedIndexPath к true.

collectionView.remembersLastFocusedIndexPath = true

Функция indexPathForPreferredFocusedView является частью UICollectionViewDelegate, поэтому может случиться так, что делегат не был назначен вашему collectionView.

Или проблема также может быть в среде фокусировки, не принимая во внимание ваш UICollectionView.

Для справки, здесь у вас есть пример простого collectionView с 5 ячейками, в котором по умолчанию изначально выбрана ячейка в центре

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

Свифт 4: У меня была такая же проблема. Я хотел перезагрузить collectionview и сосредоточиться на конкретной клетке. Если я установлю collectionView.remembersLastFocusedIndexPath = true, indexPathForPreferredFocusedView Метод делегата вызывается. Но он будет помнить последний сфокусированный indexPath, как говорит имя свойства. Это не то, что я хотел. Документы Apple сбивают с толку.

Попробуй это:

  1. Реализуйте метод делегата:

    func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath? {
    return IndexPath(item: 5, section: 0)
    }
    
  2. Принудительно обновить фокус [например: в действии кнопки]:

    collectionView.setNeedsFocusUpdate()
    collectionView.updateFocusIfNeeded()
    

Это заставит collectionView вызвать indexPathForPreferredFocusedView метод. Нам не нужно устанавливать remembersLastFocusedIndexPath к истине.

NB. У меня на экране было несколько collectionViews, и описанные выше шаги будут работать только в том случае, если в данный момент сосредоточенный collectionView и collectionView, который мы принудительно обновляем, совпадают.

Использовать preferredFocusEnvironments вернуть предпочтительный вид коллекции, который будет в фокусе, когда представление загружается [например].

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

У меня тоже была эта проблема.

Починить это:

  1. collectionView.remembersLastFocusedIndexPath = true

И после reloadData():

  1. collectionView.setNeedsFocusUpdate() collectionView.updateFocusIfNeeded()

Чтобы заставить его сфокусироваться на последнем фокусированном элементе.

Три вещи, которые решили ту же проблему, с которой я столкнулся.

  1. self.restoreFocusAfterTransition = false

  2. func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> 
        IndexPath? {
            return IndexPath(item: 3, section: 0)
        }
    
  3. override var preferredFocusEnvironments : [UIFocusEnvironment] {
            //return collectionView in order for indexPathForPreferredFocusedView method to be called.
        return [collectionView]
    }
    

У меня было два представления коллекции в двух разных контроллерах представления. Я создал подкласс UICollectionView и выполнял переход от одного контроллера представления к другому.

Из того, что я пытался, indexPathForPreferredFocusedView вызывается только когда collectionView.remembersLastFocusedIndexPath установлен в true.

Кроме того, когда collectionView перезагрузится снова, чтобы получить предпочтительный фокус.

С помощью preferredFocusEnvironments тоже хорошее решение, но у него есть свои минусы. Если твой ViewController только имея collectionViewтогда это хорошо для использования. Если в пределах ViewControllerПоведение фокуса будет другим.

Другие вопросы по тегам