1

I have user input which can contain markdown or not. I want to show the user input on screen with rendered markdown. For some reason, I want to add my own static string which does not contain markdown to the end of the user input in the blue color.

It worked when I didn't used markdown. My guess is that Data is changing the original range, which makes my code invalid.

This is my code:

let trailing = "I MUST be blue :)"
let newString = "some *user* input" + trailing
let data = newString.data(using: .utf8)!
let replacedMessageText = try! NSMutableAttributedString(markdown: data)

replacedMessageText.addAttribute(
    NSAttributedString.Key.foregroundColor,
    value: UIColor.blue,
    // Here I want to find the range of my trailing static text
    range: (newString as NSString)
        // This line is probably wrong...
        .range(of: trailing, options: .backwards)

)

This crashes with this error:

"NSMutableRLEArray objectAtIndex:effectiveRange:: Out of bounds"

I did check out the methods on Data, but none of them returns an NSRange. How can I make my code work? Markdown does not support colored text :(.

J. Doe
  • 12,159
  • 9
  • 60
  • 114
  • `newString` has two additional characters, the MarkDown tags: `*`. So its length is different from `replaceMessageText.length`. If you print `replaceMessageText.string`, you'll notice it, so the `range` is wrong – Larme Apr 29 '22 at 10:10
  • 1
    Instead, what about creating one `NSAttributedString` for each part (trailing, and the other one), with the desired effects, and then append them? – Larme Apr 29 '22 at 10:12
  • Wouldn't it be easier to just create a new blue attributed string out of `trailing`, and *then* append it to `newString`? – Sweeper Apr 29 '22 at 10:12
  • @Larme I didn't knew this was possible! It worked :) I would be happy to accept it as an answer if you post it, you were just a few seconds quicker than Sweeper – J. Doe Apr 29 '22 at 10:23
  • The main issue, would be though if you have a tag startnig on "start", and one ending on "trailing", they won't be interpreted... Just in case. – Larme Apr 29 '22 at 10:24
  • @Larme That's fine. My trailing text will never contain tag/markdown attributes :) – J. Doe Apr 29 '22 at 10:45

1 Answers1

1

For clarity of the explanation, let's do a little change:

let trailing = "I MUST be blue :)"
let newString = "some *user* input" + trailing

->

let trailing = "I MUST be blue :)"
let leading = "some *user* input"
let newString = leading + trailing

The issue is that newString is different from replacedMessageText.string, because the Markdown tags (here it's *) have been interpreted and removed. You can see it by printing replacedMessageText.string. So in your case, the range of the trailing is wrong (it's length is too big), hence the error. If you had reversed trailing and leading, it would have worked with your sample case, but you would have seen part of leading colored.

Instead, you can do:

let final = NSMutableAttributedString()
let leadingAttrStr = try NSAttributedString(markdown: Data(leading.utf8))
let trailingAttrStr = NSAttributedString(string: trailing, attributes: yourBlueAttributes)
final.append(leadingAttrStr)
final.append(trailingAttrStr)

As a note (it's not your case, but if someone encounter the issue), if you have a Markdown tag that starts on leading and ends on trailing, it won't be rendered.

Unrelated: newString.data(using: .utf8)! can be done with Data(newString.utf8), no need to force unwrap.

Larme
  • 24,190
  • 6
  • 51
  • 81