iOS: NSAttributedString - writing to data loses adaptive text color

46 Views Asked by At

I want to use text color in my NSTextVeiw / UITextView that adapts to the system appearance. Dark mode, white text, otherwise black text. I have the following NSAttributedString:

#if os(iOS)
    let testAttributedString = NSAttributedString(string: "Test", attributes: [NSAttributedString.Key.foregroundColor: UIColor.label])
#elseif os(macOS)
    let testAttributedString = NSAttributedString(string: "Test", attributes: [NSAttributedString.Key.foregroundColor: NSColor.textColor])
#endif

So I use UIColor.label on iOS and NSColor.textColor on macOS. That works well, however I have to save the typed text so I create an RTF document:

textData = try! testAttributedString.data(from: NSMakeRange(0, testAttributedString.length), documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtfd])

For some reason on iOS the text color is being converted to pure black (or pure white if dark mode is on). On macOS however, the dynamic color is being saved and also returned if I read the data back using:

let testAttributedStringFromData = try! NSAttributedString(data: textData!, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtfd], documentAttributes: nil)

If I print the color using attributedString.attributes(at: 0, effectiveRange: nil)[.foregroundColor], I get the following results:

  • on iOS:

    • before writing: <UIDynamicCatalogSystemColor: 0x600003ca6c40; name = labelColor>
    • after reading: kCGColorSpaceModelMonochrome 1 1
  • on macOS:

    • before writing: Catalog color: System textColor
    • after reading: Catalog color: System textColor

I want the same behaviour from macOS on iOS.

I've created a small sample for you to test here: https://github.com/Iomegan/Persist-Adaptive-Text-Color

1

There are 1 best solutions below

3
HangarRash On

If you just need to encode the attributed string to data but you don't specifically need it to be in RTF format, then you can archive the string with NSKeyedArchiver.

let testAttributedString = NSAttributedString(string: "Test", attributes: [NSAttributedString.Key.foregroundColor: UIColor.label])
print(testAttributedString)
do {
    let data = try NSKeyedArchiver.archivedData(withRootObject: testAttributedString, requiringSecureCoding: true)
    let newStr = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSAttributedString.self], from: data)
    print(newStr)
} catch {
    print(error)
}

This gives the output:

Test{
    NSColor = "<UIDynamicCatalogSystemColor: 0x600001714780; name = labelColor>";
}
Optional(Test{
    NSColor = "<UIDynamicCatalogSystemColor: 0x600001714780; name = labelColor>";
})