0
votes

I have a UITextView to which I have attached a gesture recognizer as follows:

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

The selector mentioned above is as follows:

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

Every time I tap the UITextView, I can see the message above printed on the console. However, the keyboard doesn't appear any more.

I found Apple's doc confusing here: Here, it says that

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

which I am interpreting as that any gestures are also sent to the view and up the chain, as is normal.

Later on the same page, it says,

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

which means that if an attached gesture recognizer is able to understand a gesture as the one it is supposed to recognize, then it will prevent it from being delivered to the view to which it is attached. Further, it specifies 3 properties that should be able to stop the gesture recognizer from doing that. I have set all three of them to false in my code, as shown above.

What is actually happening here and how do I allow the UITextView to interpret the touches normally and also be able to use a gesture recognizer?

2
Why do you need to add a gesture recognizer to the text view? What are you trying to do?rmaddy
Completely unrelated but code like if x != nil { x!... } is a bad pattern in Swift. You should do: if let x = x { x... }.rmaddy
@rmaddy I am trying to clear some place holder text, before the user can start typing. Also, agree on the pattern being bad. Still learning. Thanks for the tip.euphoria83
Are you trying to replicate the placeholder behavior of UITextField in a UITextView?rmaddy

2 Answers

2
votes

You could use the UIGestureRecognizerDelegate to make the UITapGestureRecognizer work along the regular UITextView behaviour:

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
    }

}
1
votes

The UITextView probably has its own private gesture recognizer to handle when a user taps on it. When this happens it makes the text view the first responder, which causes the keyboard to appear. Gesture recognizers can force other gesture recognizers to fail when they recognize their gesture. (See the docs) Perhaps this is what is happening when you add your tap gesture. It recognizes the tap and thus forces other gestures to fail, which prevents the text view from becoming the first responder.

The best solution is to follow the answer from this question (as was mentioned by @FrancescoDeliro in the comments) and use the delegate calls to know when editing is beginning/ending.