Detecting taps on attributed text in a UITextView while the keyboard is showing

This is a supplemental question to a previous answer of mine to the question Detecting taps on attributed text in a UITextView in iOS.

I retested the following code with Xcode 7.1.1 and iOS 9.1 and it works fine with the setup described in the answer I linked to.

  • Get the frame of the keyboard dynamically
  • Selecting a word in a UITextView
  • UITextView Custom Auto-completion
  • Counting the number of lines in a UITextView, lines wrapped by frame size
  • Bullet list and Numbered list in UITextView
  • Get X and Y coordinates of a word in UITextView
  • import UIKit
    class ViewController: UIViewController, UIGestureRecognizerDelegate {
    
        @IBOutlet weak var textView: UITextView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Create an attributed string
            let myString = NSMutableAttributedString(string: "Swift attributed text")
    
            // Set an attribute on part of the string
            let myRange = NSRange(location: 0, length: 5) // range of "Swift"
            let myCustomAttribute = [ "MyCustomAttributeName": "some value"]
            myString.addAttributes(myCustomAttribute, range: myRange)
    
            textView.attributedText = myString
    
            // Add tap gesture recognizer to Text View
            let tap = UITapGestureRecognizer(target: self, action: Selector("myMethodToHandleTap:"))
            tap.delegate = self
            textView.addGestureRecognizer(tap)
        }
    
        func myMethodToHandleTap(sender: UITapGestureRecognizer) {
    
            let myTextView = sender.view as! UITextView
            let layoutManager = myTextView.layoutManager
    
            // location of tap in myTextView coordinates and taking the inset into account
            var location = sender.locationInView(myTextView)
            location.x -= myTextView.textContainerInset.left;
            location.y -= myTextView.textContainerInset.top;
    
            // character index at tap location
            let characterIndex = layoutManager.characterIndexForPoint(location, inTextContainer: myTextView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    
            // if index is valid then do something.
            if characterIndex < myTextView.textStorage.length {
    
                // print the character index
                print("character index: \(characterIndex)")
    
                // print the character at the index
                let myRange = NSRange(location: characterIndex, length: 1)
                let substring = (myTextView.attributedText.string as NSString).substringWithRange(myRange)
                print("character at index: \(substring)")
    
                // check if the tap location has a certain attribute
                let attributeName = "MyCustomAttributeName"
                let attributeValue = myTextView.attributedText.attribute(attributeName, atIndex: characterIndex, effectiveRange: nil) as? String
                if let value = attributeValue {
                    print("You tapped on \(attributeName) and the value is: \(value)")
                }
    
            }
        }
    }
    

    However, if the UITextView settings are changed so that it is both editable and selectable

    enter image description here

    then this will cause the keyboard to display. After the keyboard is shown, the tap event handler no longer gets called. What can be done to detect taps on attributed text while the keyboard is showing?

    Update

    Although the code here is in Swift, the person who originally asked this question (in a comment to the answer I linked to above) was working with Objective-C. So I would be glad to accept an answer in either Swift or Objective-C.

    Solutions Collect From Internet About “Detecting taps on attributed text in a UITextView while the keyboard is showing”

    In your UITextView’s controller,you can implement UITextViewDelegate , Then override method

    -(BOOL)textViewShouldBeginEditing:(UITextView *)textView{
    }
    

    Inside this method you can access the textView’s selectedRange,which should also be the “tap range” of your attributed text. Then return true / false depending on your needs.