Attributed Strings & Rich Text Files

Table of Contents

  • Introduction
  • Example Code – Attributed Strings
  • Factory Functions
  • Tools & Libraries
  • Example Code – Rich Text Format (RTF)
  • Example Code – Rich Text Format Directory (RTFD)
  • Conclusion

Introduction

Attributed strings are great for creating stylised text and to make an app more visually appealing. However using them can be quite tedious, as a result one may have to write a lot of code. In this blog article, we take a look at an interesting alternative to attributed strings.

Apple Swift version 5.1.3
Xcode Version 11.3.1 (11C505).

Example Code – Attributed Strings

Here is a simple example of a UILabel containing an attributed string.

The code needed to build this simple example:

// build string 1
let string1 = "Hello "
let attrString1 = NSMutableAttributedString(string: string1)
attrString1.addAttribute(.font, value: UIFont.systemFont(ofSize: 40, weight: .bold), range: NSRange(location: 0, length: string1.count))
attrString1.addAttribute(.foregroundColor, value: UIColor.systemBlue, range: NSRange(location: 0, length: string1.count))

// build string 2
let string2 = "World!\n"
let attrString2 = NSMutableAttributedString(string: string2)
attrString2.addAttribute(.font, value: UIFont.systemFont(ofSize: 30, weight: .regular), range: NSRange(location: 0, length: string2.count))
attrString2.addAttribute(.foregroundColor, value: UIColor.systemRed, range: NSRange(location: 0, length: string2.count))

// build string 3
let string3 = "From jayway with love!"
let attrString3 = NSMutableAttributedString(string: string3)
attrString3.addAttribute(.font, value: UIFont.italicSystemFont(ofSize: 20), range: NSRange(location: 0, length: string3.count))
attrString3.addAttribute(.foregroundColor, value: UIColor.systemGreen, range: NSRange(location: 0, length: string3.count))

// join strings
let finalAttrString = NSMutableAttributedString()
finalAttrString.append(attrString1)
finalAttrString.append(attrString2)
finalAttrString.append(attrString3)

// set line spacing + align center
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 20
paragraphStyle.alignment = .center
finalAttrString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: finalAttrString.length))

// apply to label
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.attributedText = attributedString

As you can see, that is a lot of code just to get some stylized text! And changing the style will require some work depending on the style. Then imagine having to do this in a lot of places in your app – this does get quite tedious after a while!

Factory Functions

We could, of course, avoid such situation by building our own factory functions for returning ready-made attributed strings, as this will save us time and make our code reusable, especially if we want to change a style and have it take effect everywhere it is used in our app.

Tools & Libraries

We could also use some nice libraries on GitHub that can ease working with attributed strings, and a few apps on the Mac App Store for styling text that generate code. There’s even a tool online called Transformer.

But still all this requires code and in some cases a lot of code.

Example Code – Rich Text Format (RTF)

Luckily there is an alternative! A very interesting alternative which has been around for a while. We can write our text in Rich Text Format in an RTF-file using the built-in TextEdit.app on macOS and style it in there. If we want more styles, we can use for example the Pages.app.

Here is an example of the attributed string we built-in code earlier but as rich text in the TextEdit.app on macOS. I’ve styled the text as similar as possible to match the styles produced by the aforementioned code.

And here is all the code needed to read the RTF-file, as you can see it’s just a few lines :-) We can easily put this into a function and make it generic/reusable by passing in the name of the RTF-file we want it to load.

// load RTF-file
let url = Bundle.main.url(forResource: "HelloWorld-Template", withExtension: "rtf")!
let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf]
let rtfString = try! NSMutableAttributedString(url: url, options: options, documentAttributes: nil)

// apply to label
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.attributedText = rtfString

Example Code – Rich Text Format Directory (RTFD)

But wait, that’s not all! Rich text files are quite powerful, if we put an image into our document then TextEdit.app will ask us to convert the file to the RTFD-file format, and the image appears in the text. Very nice! Our document is now saved in the RTFD-file format.

Now simply modify the code to read RTFD-file format instead, so nice!

// load RTFD-file
let url = Bundle.main.url(forResource: "HelloWorld-Template", withExtension: "rtfd")!
let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtfd]
let rtfdString = try! NSMutableAttributedString(url: url, options: options, documentAttributes: nil)

// apply to label
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.attributedText = rtfdString

And there you have it, a single UILabel with an attributed string read from an RTFD-file which also contains an image! Whoa!

Conclusion

Pros

  • A good alternative.
  • Easy & simple to use.
  • Quickly make changes without writing code and need to compile.
  • Anyone can style text files, developers not needed.
  • You can create many different RTF(D)-file templates and see them visually without running your app.

Cons

  • It’s not a perfect solution at the moment.
  • Some styles don’t apply in iOS (bugs reported).
  • Font sizes don’t match. In code, the size of the font is 40, but in rich text, it is 32 – this will require some extra tweaking & testing work.

Developer Notes

  • This reminds me of the popular discussion of whether to build UI in Interface Builder or with code.
  • It does take the pain out of creating attributed strings for a cost.
  • I’ll start using this concept in projects & involve the designers I work with to see the limitations, and when needed move over to attributed strings.

Leave a Reply

Close Menu