键盘处于活动状态时显示模态视图控制器

Dan*_*anM 10 uitextfield first-responder ios presentviewcontroller

所以我基本上有一个表单,由几个文本字段组成.用户像往常一样键入字段.但是用户还可以选择双击文本字段,该文本字段呈现模态视图控制器,允许用户从与该字段相关的多个选项中进行选择.

我可以以某种方式呈现模式"在键盘上",这样当它被解除时,键盘仍然是活动的,因为在我呈现模态之前已经是第一响应者的字段?

现在,键盘在模态出现时解散,并在模态被解除时重新出现.它看起来很笨重,而且分散注意力.希望精简它,并减少屏幕上的动画量.

Rob*_*rek 11

编辑:我已经为iOS 12和Swift更新了这个答案.修订后的示例项目(包含新的Swift和更新的Objective-C实现)就在这里.


您可以创建一个新的UIWindow并将其放在默认窗口中,同时隐藏键盘的窗口.


在现有UIWindow上覆盖新UIWindow的动画示例


我在这里有一个关于Github的示例项目,但基本过程如下.

  • UIViewController为模态视图创建一个新类.我打电话给我OverlayViewController.根据需要设置相应的视图.根据你的问题,你需要传回一些选项,所以我制作了一个委托协议OverlayViewController,并将主窗口的根视图控制器(类ViewController)作为我们的委托.
protocol OverlayViewControllerDelegate: class {
  func optionChosen(option: YourOptionsEnum)
}
Run Code Online (Sandbox Code Playgroud)
  • 添加一些支持属性到我们的原始视图控制器.
class ViewController: UIViewController {
  /// The text field that responds to a double-tap.
  @IBOutlet private weak var firstField: UITextField!
  /// A simple label that shows we received a message back from the overlay.
  @IBOutlet private weak var label: UILabel!
  /// The window that will appear over our existing one.
  private var overlayWindow: UIWindow?
Run Code Online (Sandbox Code Playgroud)
  • 添加UITapGestureRecognizer到您的UITextField.
override func viewDidLoad() {
  super.viewDidLoad()

  // Set up gesture recognizer
  let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
  doubleTapRecognizer.numberOfTapsRequired = 2
  doubleTapRecognizer.delegate = self

  firstField.addGestureRecognizer(doubleTapRecognizer)

  firstField.becomeFirstResponder()
}
Run Code Online (Sandbox Code Playgroud)
  • UITextField有一个内置的手势识别器,所以我们需要允许多个UIGestureRecognizers同时操作.
extension ViewController: UIGestureRecognizerDelegate {
  // Our gesture recognizer clashes with UITextField's.
  // Need to allow both to work simultaneously.
  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                         shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
  }
}
Run Code Online (Sandbox Code Playgroud)
  • 这是有趣的部分.触发手势识别器时,创建新的UIWindow,将您的OverlayViewController作为根视图控制器分配,并显示它.请注意,我们将窗口级别设置为UIWindowLevelAlert显示在前面.但是,尽管警报窗口级别,键盘仍然在前面,因此我们也必须手动隐藏其窗口.

重要的是不要将新设置UIWindow为键或更改第一响应者UITextField或键盘将被解除.

以前(在iOS 10之前?)我们可以逃脱overlayWindow.makeKeyAndVisible(),但现在将其设置为键将解除键盘.此外,键盘的窗口现在具有非标准UIWindow.Level值,该值位于每个公开定义的值之前.我通过在层次结构中找到键盘的窗口并隐藏它来解决这个问题.

@objc func handleDoubleTap() {
    // Prepare the overlay window
    guard let overlayFrame = view?.window?.frame else { return }
    overlayWindow = UIWindow(frame: overlayFrame)
    overlayWindow?.windowLevel = .alert
    let overlayVC = OverlayViewController.init(nibName: "OverlayViewController", bundle: nil)
    overlayWindow?.rootViewController = overlayVC
    overlayVC.delegate = self

    // The keyboard's window always appears to be the last in the hierarchy.
    let keyboardWindow = UIApplication.shared.windows.last
    keyboardWindow?.isHidden = true
}
Run Code Online (Sandbox Code Playgroud)
  • 叠加窗口现在是原始窗口.用户现在可以选择您在叠加视图中构建的任何选项.在用户选择一个选项后,您的代理人应该采取您想要的任何操作,然后关闭覆盖窗口并再次显示键盘.
func optionChosen(option: YourOptionsEnum) {
  // Your code goes here. Take action based on the option chosen.
  // ...

  // Dismiss the overlay and show the keyboard
  overlayWindow = nil;
  UIApplication.shared.windows.last?.isHidden = false
}
Run Code Online (Sandbox Code Playgroud)
  • 叠加窗口应该消失,原始窗口应该出现,键盘与之前的位置相同.

  • 这是一个很好的答案.正是我正在寻找的! (2认同)