Ryohei Arai Ryohei Arai - 1 month ago 21
iOS Question

How am I able to highlight words with multiple colors in UITextVIew with NSAtrributedString?

This has been bugging me for a long time. As the title says, I do not see any other way to come with an idea to highlight some particular words with various colors. For example, if I have an array that contains words, "hello", "awesome", "hungry", "sky"...and so on. Then in the code below, it just highlights these words inside the array with one color, which is set by

NSBackgroundColorAttributeName
. So my best solution so far, which failed, was to create multiple of the same method. So I made several
highlightColors
methods and entered an array in each method and changed different color for each.

A word entered on my TextView became blue. After another word from another array was entered on my TextView, the newly entered word became red but the previous word were not highlighted anymore. I want all words from different arrays to remain highlighted differently. I never want their colors to disappear. I just cannot come up with this idea for a week. Could you please help me out with this?

func highlightColors(type: [String]){

let attrStr = NSMutableAttributedString(string: myTextView.text)

let inputLength = attrStr.string.characters.count
let searchString : NSArray = NSArray.init(array: type)
for i in 0...searchString.count-1 {

let string : String = searchString.object(at: i) as! String
let searchLength = string.characters.count
var range = NSRange(location: 0, length: attrStr.length)

while (range.location != NSNotFound) {

range = (attrStr.string as NSString).range(of: string, options: [], range: range)

if (range.location != NSNotFound) {
coloredStringArray.append(string)
attrStr.addAttribute(NSBackgroundColorAttributeName, value: UIColor.blue, range: NSRange(location: range.location, length: searchLength))
attrStr.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 20), range: range)
range = NSRange(location: range.location + range.length, length: inputLength - (range.location + range.length))
myTextView.attributedText = attrStr
myTextView.font = UIFont(name: "times new roman", size: 20.0)

}
}
}
}

Answer

Here is an example of using NSAttributedString to highlight certain words with certain colours. You can modify it any way you want to highlight with certain fonts and backgrounds, etc.

The idea is that you search the text view's text for a string then modify its attributed string with the required attributes.

The reason your attributes are probably disappearing when you edit the textView is because you are using textView.text = blah..

private var textView: UITextView!

override func viewDidLoad() {
    super.viewDidLoad()

    self.textView = UITextView()
    self.view.addSubview(self.textView)


    self.textView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
    self.textView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
    self.textView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
    self.textView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true

    self.textView.translatesAutoresizingMaskIntoConstraints = false

    self.textView.text = "Test Highlighting Colors on StackOverflow."

    self.highlight(text: ["Test", "Highlighting", "Colors", "StackOverflow", "Test"], colours: [UIColor.red, UIColor.blue, UIColor.green, UIColor.purple, UIColor.cyan])
}

func highlight(text: [String], colours: [UIColor]) {
    var ranges = Array<Int>()
    let attrString = self.textView.attributedText.mutableCopy() as! NSMutableAttributedString

    for i in 0..<text.count {

        let str = text[i]
        let col = colours[i]
        var range = (self.textView.text as NSString).range(of: str)

        while (range.location != NSNotFound && ranges.index(of: range.location) != nil) {
            let subRange = NSMakeRange(range.location + 1, self.textView.text.characters.count - range.location - 1)
            range = (self.textView.text as NSString).range(of: str, options: NSString.CompareOptions.literal, range: subRange)
        }

        if range.location != NSNotFound {
            ranges.append(range.location)
            attrString.addAttribute(NSForegroundColorAttributeName, value: col, range: range)
        }
    }

    self.textView.attributedText = attrString
}