1

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?

euphoria83
  • 14,768
  • 17
  • 63
  • 73
  • Why do you need to add a gesture recognizer to the text view? What are you trying to do? – rmaddy Jan 19 '19 at 16:52
  • 1
    Completely unrelated but code like `if x != nil { x!... }` is a bad pattern in Swift. You should do: `if let x = x { x... }`. – rmaddy Jan 19 '19 at 16:53
  • @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 Jan 19 '19 at 16:57
  • Are you trying to replicate the placeholder behavior of `UITextField` in a `UITextView`? – rmaddy Jan 19 '19 at 16:58
  • Let us know if you have a sample project for this. rmaddy is right about the pattern. For me, I would declare that gesture recognizer and all of my views lazily. – Glenn Posadas Jan 19 '19 at 16:58
  • 1
    Check this [StackOverflow answer](https://stackoverflow.com/questions/27652227/text-view-placeholder-swift) – Francesco Deliro Jan 19 '19 at 18:08
  • @FrancescoDeliro thanks for the pointer. I also want to understand why the touches are not being sent to the UITextView. – euphoria83 Jan 19 '19 at 20:41

2 Answers2

3

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
    }

}
André Slotta
  • 13,774
  • 2
  • 22
  • 34
1

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.

dunkmann00
  • 101
  • 1
  • 6
  • Thanks for the explanation. It makes sense. However, having a gesture recognizer that is not exposed and which is silently getting blocked by an explicit gesture recognizer defined by the developer seems to me like a bad design. – euphoria83 Jan 20 '19 at 01:16