8

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.

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.

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • I want to get the string that been tapped using objective c, do you know how to write that code? – Eleanor Dec 13 '15 at 01:18
  • @Liu - Did you already see the question [Detecting taps on attributed text in a UITextView in iOS](http://stackoverflow.com/questions/19332283/detecting-taps-on-attributed-text-in-a-uitextview-in-ios)? It has several answers that are in Objective-C. – Suragch Dec 13 '15 at 10:00
  • yeah, but I want to tap the certain text only once, in other words, when i tap a word, i want to remove the attribute from that word, so that i couldn't tap it twice, I didn't know how to that, did you have any idea? – Eleanor Dec 13 '15 at 10:13
  • I don't know Objective-C, but the process would be to get the range of the word at the tap location and then to remove the attribute from that range. If you can't find a Stack Overflow question and answer that tells you how to do it, then you can write your own. (And add a link in a comment here.) – Suragch Dec 13 '15 at 10:47
  • This is a touch event conflict. Here has two possible solutions: 1: add tap gesture to superview of TextField, and may set `delaysTouchesBegan` to YES, to give priority to your gesture. 2: subclass TextField and deal tap in your custom touchesBegan method. – SolaWing Dec 14 '15 at 02:49

1 Answers1

0

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.

Gocy015
  • 208
  • 3
  • 14