InputAccessoryView 中的 UITextView 未成为第一响应者(键盘最初未显示)

UITextView in InputAccessoryView not becoming First Responder (Keyboard does not show up initially)

我正在尝试实现一个输入附件视图,它的工作方式与 iOS 中的“消息”应用程序一样。我搜索了几乎所有关于该主题的 SO 问题,但找不到适合我的解决方案。

这是我创建的最小可重现代码,参考这个SO post

import UIKit

class TestViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        becomeFirstResponder()  // seems unnecessary
    }
    
    override var inputAccessoryView: UIToolbar {
        return self.keyboardAccessory
    }
    
    override var canBecomeFirstResponder: Bool {
        return true
    }

    var textView: UITextView = {
        let view = UITextView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .yellow
        return view
    }()
    
    lazy var keyboardAccessory: UIToolbar = {
        let inputAccessory = UIToolbar(frame: .init(x: 0, y: 0, width: 0, height: 100))
        inputAccessory.addSubview(textView)
        NSLayoutConstraint.activate([
            textView.centerXAnchor.constraint(equalTo: inputAccessory.centerXAnchor),
            textView.centerYAnchor.constraint(equalTo: inputAccessory.centerYAnchor),
            textView.widthAnchor.constraint(equalToConstant: 200),
            textView.heightAnchor.constraint(equalToConstant: 50)
        ])
        inputAccessory.backgroundColor = .gray
        return inputAccessory
    }()
}

我看到的每篇文章都建议覆盖 inputAccessoryViewcanBecomeFirstResponder,仅此而已。但是,在我点击 textView.

之前,键盘不会出现

任何人都可以让我知道我错过了什么吗?

编辑

正如@DonMag 所指出的,iOS 中的消息应用程序不会自动显示键盘。请考虑在 Facebook 上关注 UI。

当我按下评论按钮时,它会在弹出键盘的同时推送到另一个视图控制器。过渡效果不必完全相同,但我希望键盘在呈现的视图控制器中完全加载,就好像我在 viewDidLoad.

中调用了 becomeFirstResponder()

实际上,当您覆盖 canBecomeFirstResponder 时,键盘会出现在视图下方,这就是为什么您只能看到视图底部的辅助视图。你基本上可以通过向你的控制器添加通知来尝试这个

 override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name:UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name:UIResponder.keyboardWillHideNotification, object: nil)

}

 @objc func keyboardWillShow(notification:NSNotification) {

    let userInfo = notification.userInfo!
    let keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
    print(keyboardFrame)
    print(self.view.frame)
}

当你 运行 项目时,你会看到 keyboardWillShow 通知被雇用。(如果你删除 overiride canBecomeFirstResponder 它不会)

并且当您打印键盘和视图框架时,您会注意到键盘的 y 位置等于视图的框架高度。这意味着键盘只想向我们展示它的 accessoryView 。

因此,您需要在 keyboardWillShow 通知

中雇用 textView.becomeFirstResponder()
@objc func keyboardWillShow(notification:NSNotification) {

    let userInfo = notification.userInfo!
    let keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
    textView.becomeFirstResponder()
}

不要忘记在控制器取消时取消通知

  deinit {
    NotificationCenter.default.removeObserver(self)
}

如果您希望文本视图变为活动状态并显示键盘,视图一出现,请使用:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    textView.becomeFirstResponder()
}

如果您希望文本视图在底部可见,并在点击文本视图时激活/显示键盘,请查看此答案:


编辑

如果你想推送一个视图控制器到导航堆栈,并让键盘显示自定义输入附件视图,包含一个文本视图,并给它焦点...

向控制器添加一个隐藏文本字段。在 viewDidLoad 中告诉该文本字段使用自定义输入附件视图并告诉它成为第一响应者。

然后,在viewDidAppear中告诉自定义输入附件视图中的文本视图成为第一响应者:

class TestViewController: UIViewController {
    
    var hiddenTF = UITextField()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // set the text field to hidden
        hiddenTF.isHidden = true
        // add it to the view
        view.addSubview(hiddenTF)

        // tell hidden text field to use custom input accessory view
        hiddenTF.inputAccessoryView = keyboardAccessory
        
        // tell it to become first responder
        hiddenTF.becomeFirstResponder()
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // tell the textView (in the custom input accessory view)
        //  to become first responder
        textView.becomeFirstResponder()
    }
    
    var textView: UITextView = {
        let view = UITextView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .yellow
        return view
    }()
    
    lazy var keyboardAccessory: UIToolbar = {
        let inputAccessory = UIToolbar(frame: .init(x: 0, y: 0, width: 0, height: 100))
        inputAccessory.addSubview(textView)
        NSLayoutConstraint.activate([
            textView.centerXAnchor.constraint(equalTo: inputAccessory.centerXAnchor),
            textView.centerYAnchor.constraint(equalTo: inputAccessory.centerYAnchor),
            textView.widthAnchor.constraint(equalToConstant: 200),
            textView.heightAnchor.constraint(equalToConstant: 50)
        ])
        inputAccessory.backgroundColor = .gray
        return inputAccessory
    }()
    
}