2

I have been working on this for a long time now. I have read articles from Get currently typed word in UITextView and Get currently typed word in a UITextView and I think I am very close. My problem is that after I am able to successfully detect the '@' symbol, for some reason the word that should be returned is not getting returned. For example:

    func textViewDidChange(_ textView: UITextView) {
    commentString = commentTextView.text
    commentTextView.setTextTyping(text: commentTextView.text, withHashtagColor: .blue, andMentionColor: .red, andCallBack: callBack, normalFont: UIFont(name: "HelveticaNeue", size: 14)!, hashTagFont: UIFont(name: "HelveticaNeue-Medium", size: 14)!, mentionFont: UIFont(name: "HelveticaNeue-Medium", size: 14)!)
    if let word = textView.currentWord {
        if word.hasPrefix("@")  {
            print(word)
            print(users)
            print("Observing")
        }
    }
}

I am able to detect the "@" however, after the boolean test of .hasPrefix, the word that should follow is not being printed to the console. If I print 'word' prior to the boolean test then the correct word is printed to the console. This is the extension I am using to detect the "@" symbol.

extension UITextView {

    var currentWord : String? {
        let beginning = beginningOfDocument

        if let start = position(from: beginning, offset: selectedRange.location),
            let end = position(from: start, offset: selectedRange.length) {

            let textRange = tokenizer.rangeEnclosingPosition(end, with: .word, inDirection: 1)

            if let textRange = textRange {
                return text(in: textRange)
            }
        }
        return nil
    }
}

Any help is appreciated in advance, Thanks!

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
Chris
  • 387
  • 5
  • 18

4 Answers4

1

So you aren't currently getting the whole word in currentWord. It looks like you're only getting the @ character. currentWord is a variable and it doesn't look like you're properly iterating through all of the characters in the textField. This is how I would handle that:

if let splitText = textField.text?.split(separator: " ") {
    for word in splitText {
            if word.hasPrefix("@") {
                print(word) // call whatever function you need here
            } else {
                print("NOPE")
            }
        }
} else {
    print("No Text!!")
}

This will only fire when it finds an @ and the word contains the @ and every character after the @ until a " " (space)

Jake
  • 2,126
  • 1
  • 10
  • 23
0

You could achieve it by separating the whole textview's text by the "@":

let text = "My Text goes here @ the next text"
let separated = text.components(separatedBy: "@")

Thus get the last string from the result array (separated):

if let textAfterAt = separated.last {
    print(textAfterAt) // " the next text"
}

Note that in case the text contains more than one "@", the result would be the string after the last "@", so for instance:

let text = "My Text goes here @ the next text @ the last text after the at"
let separated = text.components(separatedBy: "@")
if let textAfterAt = separated.last {
    print(textAfterAt) // " the last text after the at"
}
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
0

My preference in this case would be to use a regular expression but it seems much more complicated than I remember. See the code below (playground code)

#1
let testString1: NSString = "This is a test of a @single prepended word"
let testString2: NSString = "This is a test of a @two @prepended words"

let regEx = try? NSRegularExpression(pattern: "\\@\\w+")
let results = regEx?.matches(in: String(testString2), options: [], range: NSMakeRange(0, testString2.length))

for result in results! {
    let word = testString2.substring(with: result.range)
    // #2
    print(word)
}

Output

@two

@prepended

#1 I could only achieve this using NSStrings because of the recent Swift 4 changes to String (String is a collection of characters again).

#2 you now have each word and the range of the word in the String, so you can now do some work/calculations and replace each word with whatever you want.

Scriptable
  • 19,402
  • 5
  • 56
  • 72
0

Use the delegate to get updates as characters are typed. Use a regex to find all words beginning with @ and then apply the desired attributes to each match and assign the attributed string back to the textfield.

    class ViewController: UIViewController {
        let regex = try! NSRegularExpression(pattern: "\\@(\\w+)")
        let highlightAttributes = [NSForegroundColorAttributeName : UIColor.blue]
    }

    extension ViewController: UITextFieldDelegate {
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            guard let originalText = textField.text as? NSString else {
                return false
            }
            let proposedText = originalText.replacingCharacters(in: range, with: string)
            let matches = regex.matches(in: proposedText, options: [], range: NSRange(location: 0, length: proposedText.count))
            let attributedText = NSMutableAttributedString(string: proposedText)
            matches.forEach {attributedText.addAttributes(self.highlightAttributes, range: $0.range)}
            textField.attributedText = attributedText
            return false
        }
    }
Josh Homann
  • 15,933
  • 3
  • 30
  • 33