Keeping track of and changing color of text in UITextView

Viewed 157

As the user is types in a textview, I am looking at each word and see it matches words in an array I have. If there's a match, the word is changed to blue and a boolean variable didFindACertainWord is set to true (to make sure only one word is blue). I was able to do this part successfully with a few errors:

  1. The certain word I changed to blue works, but the words typed before font is changed and anything I type after the word is blue too (which I do not want). I only want to change the certain word to blue and keep the other words black color and same font as before.

  2. I can not figure out how to find out if the user deleted the certain word. If they do, I want to change the color of the certain word back to to black (after they deleted the first character of the certain word) and set didFindACertainWord to false.

Here's my current code in my textViewDidChange method:

func textViewDidChange(_ textView: UITextView) {
    //get last word typed
    let size = textView.text.reversed().firstIndex(of: " ") ?? textView.text.count
    let startWord = textView.text.index(textView.text.endIndex, offsetBy: -size)
    let lastWord = textView.text[startWord...]

    //check if last word is in array and we did not already find one
    if certainWords.contains(String(lastWord)) && !didFindACertainWord {
        didFindACertainWord = true

        //change color of the word
        let attributedString = NSMutableAttributedString.init(string: textView.text)
        let range = (textView.text as NSString).range(of: String(lastWord))
        attributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.blue, NSAttributedString.Key.font: UIFont(name: "Avenir-Roman", size: 18)], range: range)
        textView.attributedText = attributedString
    }
}

What am I missing / how can I do this successfully? P.S. the font of all text in the textview should be UIFont(name: "Avenir-Roman", size: 18)

I am searching each word because after the user types an action word, I need to read the next words if they're related to the action word to bold them. For example if the user types "see Paris London Berlin to find the best food", the action word is "see" and the related words to be bolded are the places "Paris Italy France" and the words not related (will be in regular font) are "to find the best"

1 Answers

For the first issue, it's because you should do an "else" case, and reset the color and the boolean value. You should add:

} else {
    didFindACertainWord = false
    textView.attributedText = attributedString
}

For the second one, you shouldn't need to keep working only on the last word, instead check if there is a match in the whole string.

Not tested, but it should to the work:

func textViewDidChange(_ textView: UITextView) {

    let attributedString = NSMutableAttributedString(string: textView.text,
                                                     attributes: [.font: UIFont(name: "Avenir-Roman", size: 18)])

    let allWords = attributedString.string.components(separatedBy: CharacterSet.whitespaces)
    if let firstMatch = allWords.first(where: { return certainWords.contains($0)}) {
        didFindACertainWord = true
        let firstMatchRange = (attributedString.string as NSString).range(of: firstMatch)
        attributedString.addAttribute(.foregroundColor, value: UIColor.blue, range: firstMatchRange)
    } else {
        didFindACertainWord = false
    }
    textView.attributedText = attributedString
}
Related