Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get tapped word in a UITextview

I have added a uitextview which is initially non editable. I added a tap gesture which enable the editing to true. In the tap gesture selector I get the word that is being tapped. I have tried a lot many solution but none worked for me as a complete solution. Every solution worked if the textview is not scrolled. But if I scroll the textview the exact word is not retrieved. Here is my code for getting the tapped word:

 @objc func handleTap(_ sender: UITapGestureRecognizer) {

    notesTextView.isEditable = true
    notesTextView.textColor = UIColor.white

    if let textView = sender.view as? UITextView {

        var pointOfTap = sender.location(in: textView)
        print("x:\(pointOfTap.x) , y:\(pointOfTap.y)")

        let contentOffsetY = textView.contentOffset.y
        pointOfTap.y += contentOffsetY
        print("x:\(pointOfTap.x) , y:\(pointOfTap.y)")
        word(atPosition: pointOfTap)

 }

func word(atPosition: CGPoint) -> String? {
    if let tapPosition = notesTextView.closestPosition(to: atPosition) {
        if let textRange = notesTextView.tokenizer.rangeEnclosingPosition(tapPosition , with: .word, inDirection: 1) {
            let tappedWord = notesTextView.text(in: textRange)
            print("Word: \(tappedWord)" ?? "")
            return tappedWord
        }
        return nil
    }
    return nil
}

EDITED:

Here is the demo project with the problem. https://github.com/amrit42087/TextViewDemo

like image 445
Amrit Sidhu Avatar asked Mar 07 '23 01:03

Amrit Sidhu


2 Answers

The best and easiest way in Swift 4

METHOD 1:

Step 1: Add Tap Gesture on the textview

let tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(recognizer:)))

textViewTC.addGestureRecognizer(tap)

Step 2: Implement Tap Gesture

@objc func tapResponse(recognizer: UITapGestureRecognizer) {
        let location: CGPoint = recognizer.location(in: textViewTC)
        let position: CGPoint = CGPoint(x: location.x, y: location.y)
        let tapPosition: UITextPosition = textViewTC.closestPosition(to: position)!
        guard let textRange: UITextRange = textViewTC.tokenizer.rangeEnclosingPosition(tapPosition, with: UITextGranularity.word, inDirection: 1) else {return}

        let tappedWord: String = textViewTC.text(in: textRange) ?? ""
        print("tapped word ->", tappedWord)
    }

And yes thats it. Go for it.

METHOD 2:

The alternate way is that you can enable links for textview and then set the same as an attribute. Here is an example

var foundRange = attributedString.mutableString.range(of: "Terms of Use") //mention the parts of the attributed text you want to tap and get an custom action
attributedString.addAttribute(NSAttributedStringKey.link, value: termsAndConditionsURL, range: foundRange)

set this attribute text to Textview and textView.delegate = self

Now you just need to handle the response in

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

Hope it helps you. All the best.

like image 62
Kunal Gupta Avatar answered Mar 14 '23 23:03

Kunal Gupta


You don't need to add the content offset of the text view. When you convert location into a scrollview it will already take its content offset into account.

Removing:

let contentOffsetY = textView.contentOffset.y
pointOfTap.y += contentOffsetY

should work.

like image 42
beyowulf Avatar answered Mar 14 '23 23:03

beyowulf