Search code examples
iosswiftuilabelnsattributedstringnsmutableattributedstring

End of NSAttributedString with emojis left unformatted


The end of my NSAttributedString with emojis in the contents is not being formatted. I'm trying to format the entire string (set the text color to white in this example, for simplification), but some of the text stays unformatted when put in a UILabel.

enter image description here

Currently, I'm using

let attributedString = NSMutableAttributedString(string: contents)

attributedString.addAttribute(
    NSForegroundColorAttributeName,
    value: UIColor.white,
    range: NSMakeRange(0, contents.characters.count)
)

label.attributedText = attributedString

I've also tried getting the length by using contents.utf8.count but get the same result.

I noticed that the number of unformatted characters is the same as the number of emojis in the string. Could that have something to do with what's going on?


Solution

  • String.characters.count returns the number of rendered characters in the string. Some emojis (such as flags and race-specific ones) are a combination of two or more UTF characters that are rendered into one character, in order to allow for more emojis.

    UTF stands for Unicode Transformation format, or Unicode for short. It allows for computers, phones, tablets, and everything else electronic to use the same standardized set of characters.

    Those who implement it can chose how to render the text, but it is massively important that devices communicate using a standardized character set. Otherwise, sending the message "Hello, World" to someone may show up as "Ifmmp, Xpsme"

    In order to get the actual length of the string for use in NSMakeRange, use NSAttributedString.length or Int("\(contents.endIndex)").

    So, the code should look like this

    let attributedString = NSMutableAttributedString(string: contents)
    
    attributedString.addAttribute(
        NSForegroundColorAttributeName,
        value: UIColor.white,
        range: NSMakeRange(0, attributedString.length)
    )
    
    label.attributedText = attributedString
    

    This will now produce the correctly formatted text

    enter image description here