Search code examples
iosswiftstringuitextviewnsmutableattributedstring

String and AtttributedString in the same UITextView


Can I use a String and NSMutableAttributedString in the same UITextView?

I am importing a .docx file and converting to String I am then displaying that in a UITextField however I want to colour specific words. Ideally the user would type "LineBreak" and it would automatically change the word LineBreak to a different colour

It is my understanding this will need to use NSMutableAttributedString however I don't know how to go about doing this

let string = "Test Specific Colour LineBreak TestLine2"
let attributedString = NSMutableAttributedString.init(string: string)
let range = (string as NSString).range(of: "LineBreak")
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, 
value: UIColor.blue, range: range)
txtView.attributedText = attributedString

So using the example above, I want to have the colour of "LineBreak" change whenever it is typed. The above works to change the colour but not every time I type it. I need to recognise that the string "LineBreak" is present and change its colour

What is the best way to achieve what I'm after?


Solution

  • Here's one approach to get you started...

    Make sure your controller conforms to UITextViewDelegate and you've assigned the text view's delegate:

    class ExampleViewController: UIViewController, UITextViewDelegate {
    
        @IBOutlet var theTextView: UITextView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            theTextView.delegate = self
            theTextView.text = "Test Specific Colour LineBreak TestLine2 linebreak and another LineBreak occurance"
            formatTextInTextView(theTextView)
        }
    
        func textViewDidChange(_ textView: UITextView) {
            formatTextInTextView(textView)
        }
    
        func formatTextInTextView(_ textView: UITextView) -> Void {
            guard let curText = textView.text else { return }
    
            let bScroll = textView.isScrollEnabled
            textView.isScrollEnabled = false
            let selRange = textView.selectedRange
    
            let attributedString = NSMutableAttributedString.init(string: curText)
    
            let regex = try! NSRegularExpression(pattern: "LineBreak", options: [.caseInsensitive])
            let items = regex.matches(in: curText, options: [], range: NSRange(location: 0, length: curText.count))
            let ranges: [NSRange] = items.map{$0.range}
            for r in ranges {
                attributedString.addAttribute(.foregroundColor, value: UIColor.red, range: r)
            }
            textView.attributedText = attributedString
            textView.selectedRange = selRange
            textView.isScrollEnabled = bScroll
        }
    
    }
    

    You may want to limit it to case-sensitive, whole-word, etc...