Search code examples
iosarraystextviewswift3nsattributedstring

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)

            }
        }
    }
} 

Solution

  • 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
    }