Search code examples
swiftuikituitextviewnewlinensattributedstring

Swift attribute for new line in NSAttributedString


I have a UITextView with a lightGray text.

I would like the color to always be set to black with each new line so that I can be sure that the new line is always black regardless of the color of the previous line

For example

This is first line --> foregroundColor.lightgray

\n

_ --> this new empty line must have foregroundColor.black

To do this I tried to change the foregroundColor attribute every time I hit enter (new line \n)

In Coordinator of UITextView

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if text == "\n" {
                let customAttrributes : [NSAttributedString.Key: Any] = [
                    NSAttributedString.Key.foregroundColor: UIColor.black
                ]
                if textView.selectedTextRange != nil {
                   textView.textStorage.addAttributes(customAttrributes, range: NSRange(location: textView.selectedRange.location, length: 1))

This return an "Out of bounds" error and I don't understand why. I suppose it is due to the fact that since there is no character, length 1 is considered non-existent. But then is it possible to set the new blank line already with the black text attribute or do you have to wait for some characters to be inserted?

Thanks!


Solution

  • One of easiest way to do it is to set typingAttributes. You can detect a new line using this answer for example. So let's say what we started printing with .lightGray color. Firstly we set: textView.typingAttributes = [.foregroundColor: UIColor. lightGray]. We should switch to the black color on a start of a new line.

    var previousRect = CGRect.zero
    func textViewDidChange(_ textView: UITextView) {
        let pos = textView.endOfDocument
        let currentRect = textView.caretRect(for: pos)
        self.previousRect = self.previousRect.origin.y == 0.0 ? currentRect : self.previousRect
        if currentRect.origin.y > self.previousRect.origin.y {
            textView.typingAttributes = [.foregroundColor: UIColor.black]
        }
        self.previousRect = currentRect
    }