【问题标题】:UITextView in InputAccessoryView not becoming First Responder (Keyboard does not show up initially)InputAccessoryView 中的 UITextView 没有成为第一响应者(键盘最初不显示)
【发布时间】:2022-01-03 15:50:46
【问题描述】:

我正在尝试实现一个输入附件视图,其工作方式与 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()

【问题讨论】:

    标签: ios swift uikit uitextview


    【解决方案1】:

    如果您希望文本视图激活并显示键盘,视图一出现,请使用:

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

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

    https://stackoverflow.com/a/61508928/6257435


    编辑

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

    向控制器添加一个隐藏文本字段。在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
        }()
        
    }
    

    【讨论】:

    • 好吧,UIGestureRecognizer 没有必要在点击时显示键盘。它只适用于示例代码。我希望键盘出现在视图被推送之前。或者至少过渡需要足够平滑。
    • 如果用户按下视图控制器然后返回,这也可以。正如您所知道的,当导航堆栈中的 viewcontroller 时,如果用户向左滑动但不关闭页面 viewDidappear 也会被雇用。这不会是个问题吗?
    • @shinhong - 你指的是我链接到的 other 答案吗?点击手势在那里,因为在该示例中它还显示/隐藏/激活文本视图。
    • @shinhong - 你需要更好地描述你的要求......在你原来的帖子中,你说 “就像 iOS 中的 Messages 应用一样工作” .. . 但是,在 iOS 消息应用程序中,如果您选择 现有 消息,键盘将不会自动显示。
    • @DonMag 是的,我评论的第一句话是关于您提供的链接。对困惑感到抱歉。您提供的解决方案确实有效,但在我的模拟器中,视图加载和键盘显示之间的延迟相当长(~0.5s),并且输入附件视图的 y 轴过渡效果不稳定。
    【解决方案2】:

    实际上,当您覆盖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 位置等于视图的框架高度。这意味着键盘只想向我们显示它的附件视图。

    所以,你需要在keyboardWillShow通知中雇佣textView.becomeFirstResponder()

    @objc func keyboardWillShow(notification:NSNotification) {
    
        let userInfo = notification.userInfo!
        let keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        textView.becomeFirstResponder()
    }
    

    控制器deinit时不要忘记deinit通知

      deinit {
        NotificationCenter.default.removeObserver(self)
    }
    

    【讨论】:

    • 感谢您的解释。那么textView.becomFirstResponder() 不能在viewDidLoad 中工作的原因是什么?我试过viewDidLoadviewWillAppearviewWillLayoutSubViewsviewDidLayoutSubviews,但只有在viewWillLayoutSubViews之后它才开始工作(有一些小故障)。
    • 因为生命周期。 viewdidloadoverride var inputAccessoryView 之前被雇用,这就是为什么将 becomeFirstresponder 添加到文本字段(未添加到视图层次结构)是没有意义的。如果我们来了`viewWillLayoutSubViews`,我们无法保证这个函数调用了多少次。也许有 1 次,它在 `textifeld` 添加到 hieararcy 之后调用,这就是它起作用的原因。
    • 我明白了。但我想知道添加NotificationCenter 是否是这里的最佳做法。当从另一个 VC 推送到TestViewController 时,它会产生令人不快的过渡效果。我只是希望我可以在viewDidLoad 中使用becomeFirstResponder 作为任何其他textViews :(
    • 如果此页面必须有键盘,则不要声明惰性键盘附件。只需将您的文本字段及其附件视图添加到 viewdidload 中,然后在 viewdidload 中调用 becomeFirstResponder。它会解决你的问题。并且不需要覆盖这些方法。
    • 您能否详细说明如何在不覆盖的情况下添加输入附件视图?为了更具体地说明我的要求,是否在视图加载时显示键盘取决于用户如何推动视图控制器(即用户点击了哪个按钮)。
    猜你喜欢
    • 2012-09-22
    • 1970-01-01
    • 2015-07-15
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多