1

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!

Stefano Vet
  • 567
  • 1
  • 7
  • 18
  • I think the problem is that `textView.selectedRange.location` is probably at the end of the `NSAttributedString`, so when you set a attribute range starting there with a length of 1, you're going past the end - that is, out of bounds. If you back-arrow (or click) to some position earlier in the text and insert a new line there, I would guess that you wouldn't get the error. – Chip Jarred Oct 15 '22 at 10:47

2 Answers2

1

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
}
Bulat Yakupov
  • 440
  • 4
  • 13
  • thanks Bulat I had already tried to set the black color already in the textStorage. In reality, when a new line is created within an already set attribute (foregroundColor.lightgray) the text wraps but does not open a new attribute, but simply continues with the attribute that already exists in the previous line. Trying to create a new one as you can see from my example, it is not possible due to an "out of bonds" error. Thanks | – Stefano Vet Oct 15 '22 at 08:22
  • I added one more idea which can actually be a better solution for you :) – Bulat Yakupov Oct 15 '22 at 08:25
  • I have already used shouldChangeTextIn however as you see in the example. The problem is not intercepting the new line but unfortunately I don't think it is possible to add an attribute to a new line that is still empty. Thanks so much! – Stefano Vet Oct 15 '22 at 08:38
  • 1
    In order to set attributes for text your are going to insert you need to use typingAttributes – Bulat Yakupov Oct 15 '22 at 08:41
  • Thanks Bulat!! Simply add textView.typingAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black] in shouldChangeTextIn solves the problem! If you want to answer, so i can set the answer as accepted! – Stefano Vet Oct 15 '22 at 09:03
  • No problem. Yes, please :) – Bulat Yakupov Oct 15 '22 at 09:04
  • ok. I await your response then. Thank you again – Stefano Vet Oct 15 '22 at 09:09
  • Stefano, I updated my answer :) – Bulat Yakupov Oct 15 '22 at 11:31
  • in my example I just removed .addAttribute and added .typingAttributes. Update your replay like this if you agree: func textView (_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { if text == "\ n" { textView.typingAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black] – Stefano Vet Oct 15 '22 at 11:50
  • \n is not a convenient way to check for a new line. New line can appear without that if there are too many characters. So I will keep my answer as is, but you can write your code as you wish. – Bulat Yakupov Oct 15 '22 at 12:08
0

This is an example for two line with different text and textColor in NSAttributedString:

 let attributedString = NSMutableAttributedString(attributedString: NSAttributedString(string: "First Line", attributes: [.font: UIFont.systemFont(ofSize: 16, weight: .semibold), .foregroundColor: UIColor.lightGray]))
    attributedString.append(NSAttributedString(string: "Second Line", attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 16, weight: .semibold)]))
textView.attributedText = attributedString
Fabio
  • 5,432
  • 4
  • 22
  • 24
  • The problem is that I don't know the second line text being a text editor. I should always start with black even if the row is empty. It should always be borne in mind that even if I divide a gray sentence, the part that goes into the new line should become black. Thanks! – Stefano Vet Oct 15 '22 at 07:22