AttributedString Tutorial for Swift: Getting Started
Learn how to format text and create custom styles using iOS 15’s new AttributedString value type as you build a Markdown previewer in SwiftUI. By Ehab Amer.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
AttributedString Tutorial for Swift: Getting Started
30 mins
- Getting Started
- AttributedString vs. NSAttributedString
- Using Markdown
- Examining the Structure of an AttributedString
- Characters and Indices
- Runs
- Applying the Themes
- Defining Theme Styles
- Creating Custom Attributes
- Attribute Scopes
- Rendering Custom Attributes
- Saving Styled Strings
- Saving Custom Attributes
- Saving Fonts
- Where to Go From Here?
Building great-looking apps doesn’t rely on just images — it also extends to text. Different styles in attributed strings can go a great distance to making information more appealing. In this tutorial, you’ll learn about the new AttributedString value type introduced in iOS 15 and macOS 12. You’ll also see how to leverage its capabilities, including formatting with Markdown, to do more with text in your apps.
This tutorial will cover:
- Differences between the new AttributedString and the older NSAttributedString that’s bridged from Objective-C.
- Formatting and styling an attributed string using Markdown.
- The structure of an attributed string and how to alter it.
- Creating and rendering custom attributes.
- Encoding and decoding an attributed string and its custom attributes.
Getting Started
Download the starter project by clicking Download Materials at the top or bottom of the tutorial.
The app you’ll build, Markdown Preview, allows you to type a basic text string that it then converts to an attributed string. Then, it saves this attributed string to a library of your creation.
Start by opening MarkdownPreview.xcodeproj in the starter folder. Build and run the app to see your starting point.
The first section of the screen allows you to choose a theme. A group of themes is already included in the project but won’t have any effect yet.
You’ll divide the work on this app into five parts:
- Converting a Markdown string to an attributed string.
- Applying the themes on the text without permanently changing its attributes.
- Creating custom attributes that can be part of your Markdown.
- Creating a text view that can render the new custom attributes.
- Saving your attributed string into a library.
AttributedString vs. NSAttributedString
Before you start working on the project, it’s worth knowing a few things about AttributedString in comparison with the older NSAttributedString. Specifically, it:
- Is a first-class Swift citizen and takes advantage of Swift features, similar to the differences between String and NSString.
- Is a value type, while the older NSAttributedString is a reference type.
- Conforms to
Codable
. You can directly encode and decode an AttributedString object along with its attributes just like working with a normal String. - Has the same character-counting behavior as String.
- Is fully localizable. You can even define styles in your text directly in the localization files!
- Most importantly, AttributedString has full support for Markdown.
Using Markdown
Markdown is a popular markup language for formatting text. It can format whole documents — not just paragraphs. You might be surprised to learn that all the books published here on raywenderlich.com are written entirely in Markdown. :]
Write a Markdown string in the Raw Markdown text field, and notice that the text appears as-is in the Rendered Markdown area. The Markdown attributes aren’t translated to style the text yet.
Open MarkdownView.swift in the Views group, then go to convertMarkdown(_:)
. This method handles converting your raw text to an AttributedString
. Your text isn’t treated as Markdown on its own if you use the standard initializer AttributedString(_:)
. Change the implementation of the method to:
// 1
guard var attributedString = try? AttributedString(markdown: string) else {
// 2
return AttributedString(string)
}
// 3
printStringInfo(attributedString)
// 4
return attributedString
The code you added does the following:
- Tries to convert the raw string to an attributed string using the initializer
AttributedString(markdown:)
. - If it fails, then it creates an attributed string using the default initializer without any Markdown styling.
- Prints some information about the attributed string. This method is currently empty. You’ll implement it in the next section.
- Returns the attributed string that succeeded in the Markdown initializer.
Build and run. Enter the same Markdown string you tried before in the Raw Markdown area, and see how it appears now:
Examining the Structure of an AttributedString
An AttributedString object consists of characters and you can count them just like a normal string. But it also consists of AttributedString.Runs
.
Runs are the parts of an AttributedString
that describe what style applies to which characters in the text. Each run consists of a range for a substring and the styles applied to it. If your text is plain and has no styles, then your attributed string will consist of only one run. If your AttributedString
uses a variety of styles, then it’ll be broken down into many runs. You’ll get deeper into runs shortly.
Characters and Indices
To get a better idea of what characters are, return to Views/MarkdownView.swift, then go to printStringInfo(_:)
. Implement it as follows:
// 1
print("The string has \(attributedString.characters.count) characters")
// 2
let characters = attributedString.characters
// 3
for char in characters {
print(char)
}
Here’s what’s happening in the code above:
- Print the number of characters in the attributed string.
- Create a variable holding the
AttributedString.CharacterView
that you’ll iterate over to get the value of each character separately. - Iterate over this collection and prints the value of the characters one by one.
Build and run. Enter this raw Markdown string to try it out:
This is **Bold text** and this is _italic_
You’ll see the output from printStringInfo(_:)
in Xcode’s console:
The string has 36 characters
The original string is 42 letters. But when treated as Markdown, the ** and _ characters that are part of the Markdown syntax are no longer part of the actual string. They became attributes or style, not content.
Scroll up a little in the log, and you’ll find that the previous log on the character count is 37. That happened right before you entered the last _, when the string was:
This is **Bold text** and this is _italic
You hadn’t entered the closing _ for the italic syntax, so AttributedString wasn’t treating this part as italic yet. Both opening and closing characters must be present, otherwise they’re considered part of the content.
Notice that the italic style isn’t applied, and the _ is part of the content in the attributed string.