UIKeyCommands не работают, когда посредник viewController содержит два viewControllers

Я считаю, что это нетривиальная проблема, связанная с UIKeyCommands, иерархией ViewControllers и/или респондентов.

В моем приложении iOS 9.2 у меня есть класс с именем NiceViewController, который определяет UIKeyCommand, что приводит к печати чего-либо на консоли.

Здесь NiceViewController:

class NiceViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()

    let command = UIKeyCommand(input: "1", modifierFlags:UIKeyModifierFlags(), 
                   action: #selector(keyPressed), discoverabilityTitle: "nice")

    addKeyCommand(command)
  }

  func keyPressed() {
    print("works")
  }
}

Когда я добавляю, что NiceViewController как единственный дочерний элемент моего основного контроллера представления работает правильно - нажатие кнопки "1" на внешней клавиатуре (физическая клавиатура при использовании в симуляторе) работает как шарм. Однако, когда я добавляю контроллер второго представления в свой главный контроллер просмотра, UIKeyCommands, определенный в NiceViewController, перестает работать.

Мне бы хотелось понять, почему это происходит, и как обеспечить, чтобы наличие нескольких контроллеров дочерних представлений в моем основном контроллере представления не мешало этим контроллерам дочерних представлений обращаться с UIKeyCommands.

Вот мой главный контроллер:

class MainViewController: UIViewController {
  let niceViewController = NiceViewController()
  let normalViewController = UIViewController()

  override func viewDidLoad() {
    super.viewDidLoad()


    self.view.addSubview(niceViewController.view)
    self.addChildViewController(niceViewController)

    self.view.addSubview(normalViewController.view)
    // removing below line makes niceViewController accept key commands - why and how to fix it?
    self.addChildViewController(normalViewController)

  }
}    

Ответ 1

Я не считаю, что это проблема с UIKeyCommands

В iOS только один контроллер просмотра может управлять ключевыми командами. Таким образом, с вашей настройкой у вас есть контроллер контейнера с двумя контроллерами дочерних представлений. Вы должны сказать iOS, что вы хотите, чтобы NiceViewController имел контроль над командами клавиш.

Определение первых ответчиков

На высоком уровне, чтобы поддерживать ключевые команды, вы должны не только создать UIKeyCommand и добавить его в контроллер просмотра, но также вы должны включить свой контроллер представления в качестве первого ответчика, чтобы он мог для ответа на команды клавиш.

Во-первых, в любом контроллере вида, для которого вы хотели бы использовать ключевые команды, вы должны позволить iOS знать, что этот контроллер может стать первым ответчиком:

override func canBecomeFirstResponder() -> Bool {
    // some conditional logic if you wish
    return true
}

Затем вам нужно убедиться, что VC фактически становится первым ответчиком. Если какие-либо VC содержат какие-то текстовые поля, которые становятся респондентами (или что-то подобное), то VC, вероятно, сам станет первым ответчиком, но вы всегда можете вызвать becomeFirstResponder() on NiceViewController, чтобы он стал первым ответчиком и, между прочим, отвечать на команды клавиш.

Смотрите документы для UIKeyCommand:

Система всегда имеет первую возможность обрабатывать ключевые команды. Ключевые команды, которые сопоставляются с известными системными событиями (например, вырезание, копирование и вставка), автоматически направляются в соответствующие методы ответа. Для других ключевых команд UIKit ищет объект в цепочке ответчиков с ключевым командным объектом, который соответствует нажатым клавишам. Если он найдет такой объект, он затем проведет цепочку ответчиков, ища первый объект, который реализует соответствующий метод действия, и вызывает первый найденный.

Примечание.. В то время как кто-то взаимодействует с другим VC , и это первый ответчик, NiceViewController не может быть первым ответчиком одновременно, поэтому вы можете хотите также некоторые команды клавиш на другом VC.

Почему это не всегда необходимо

Когда представлен только один VC, iOS полагает, что он будет первым ответчиком, но когда у вас есть VC контейнера, похоже, что iOS обрабатывает контейнер в качестве первого ответчика, если нет ребенка, который говорит, что он способен чтобы стать первым ответчиком.