当中间viewController包含两个viewControllers时,UIKeyCommands不起作用

时间:2016-08-29 22:18:28

标签: ios swift uiviewcontroller first-responder becomefirstresponder

我认为这是一个与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)

  }
}    

3 个答案:

答案 0 :(得分:4)

我认为这不是UIKeyCommands

的问题

在iOS中,一次只能有一个View Controller可以管理关键命令。因此,对于您的设置,您有一个带有几个子视图控制器的容器视图控制器。你应该告诉iOS你希望NiceViewController能够控制关键命令。

定义第一响应者

在较高级别,为了支持关键命令,您不仅必须创建UIKeyCommand并将其添加到视图控制器,还必须使视图控制器成为第一响应者,以便它能够响应关键命令。

首先,在您想要使用关键命令的任何视图控制器中,您应该让iOS知道该控制器能够成为第一响应者:

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

接下来,您需要确保VC确实成为第一个响应者。如果任何VC包含某些成为响应者(或类似内容)的文本字段,那么VC可能会成为第一个响应者,但您可以随时在becomeFirstResponder()上调用NiceViewController使其成为第一响应者,除其他外,响应关键命令。

请参阅UIKeyCommand的文档:

  

系统始终有第一次处理关键命令的机会。映射到已知系统事件(例如剪切,复制和粘贴)的关键命令会自动路由到相应的响应方法。对于其他键命令,UIKit使用与按下的键匹配的键命令对象在响应器链中查找对象。如果找到这样的对象,它会遍历响应者链,寻找实现相应操作方法的第一个对象,并调用它找到的第一个对象。

注意:当有人与其他VC 交互并且它是第一个响应者时,NiceViewController不能同时成为第一个响应者,所以你也可能想要在另一个VC上使用一些关键命令。

为什么这不是必要的

当只呈现一个VC时,iOS似乎认为它将是第一个响应者,但是当你有一个容器VC时,iOS似乎将容器视为第一个响应者,除非有一个孩子说它能够成为第一响应者。

答案 1 :(得分:1)

关注@Matthew解释解决方案是添加becomeFirstResponder()请求;在viewDidAppear而不是viewDidLoad解决了我的类似问题。 Swift4

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    becomeFirstResponder()
    print("becomeFirstResponder?: \(isFirstResponder)")
}

答案 2 :(得分:0)

我在尝试进行此操作时发现,如果您在子视图控制器上手动调用becomesFirstResponder(),它将允许您拥有多个第一响应者,并且在命中命令时会显示所有关键命令。

我不确定为什么这样做的确能确保您一次只能拥有一个firstResponder