UITextView 的 UIGestureRecognizer 阻止键盘在点击时出现

UITextView's UIGestureRecognizer preventing keyboard from appearing on tap

我有一个 UITextView,我附加了一个手势识别器,如下所示:

let testTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(textTextViewTapped(gestureRecognizer:)))
testTapGestureRecognizer.cancelsTouchesInView = false
testTapGestureRecognizer.delaysTouchesBegan = false
testTapGestureRecognizer.delaysTouchesEnded = false
if textTextView != nil {
    textTextView!.addGestureRecognizer(testTapGestureRecognizer)
}

上面提到的选择器如下:

@objc func textTextViewTapped(gestureRecognizer: UIGestureRecognizer) {
    print("testTextViewTapped called.")
}

每次点击 UITextView 时,我都可以在控制台上看到上面打印的消息。但是,键盘不再出现了。

我发现 Apple 的文档在这里令人困惑: Here,它表示

A gesture recognizer doesn’t participate in the view’s responder chain.

我将其解释为正常情况下任何手势也会发送到视图和链上。

稍后在同一页上,它说,

If a gesture recognizer recognizes its gesture, the remaining touches for the view are cancelled.

这意味着如果附加的手势识别器能够将手势理解为它应该识别的手势,那么它将阻止将其传递到它所附加的视图。此外,它指定了 3 个应该能够阻止手势识别器执行此操作的属性。我已经在我的代码中将它们全部设置为 false,如上所示。

这里实际发生了什么,我如何让 UITextView 正常解释触摸并能够使用手势识别器?

当用户点击它时,UITextView 可能有自己的私有手势识别器来处理。发生这种情况时,它会使文本视图成为第一响应者,从而导致键盘出现。手势识别器可以强制其他手势识别器在识别他们的手势时失败。 (参见 docs)也许这就是当您添加点击手势时发生的情况。它识别点击并因此强制其他手势失败,从而防止文本视图成为第一响应者。

最好的解决方案是遵循这个 question 的答案(正如@FrancescoDeliro 在评论中提到的那样)并使用委托调用来了解何时编辑 beginning/ending.

您可以使用 UIGestureRecognizerDelegate 使 UITapGestureRecognizer 按照常规 UITextView 行为工作:

class TestViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap))
        tapGestureRecognizer.delegate = self

        textView.addGestureRecognizer(tapGestureRecognizer)
    }

    @objc private func tap() {
        print("tap")
    }

}

extension TestViewController: UIGestureRecognizerDelegate {

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

}