Fluent Attributed Strings
One thing all iOS developers have to deal with is styling text. There are many ways to go about achieving this. You could do it graphically via interface builder. Programmatically you could use UILabel’s exposed properties such as `font` and `textColor` or you could use attributed strings. Let’s explore the latter option.
These days I have been using attributed strings to style my text as you can build a really neat API that is very readable given the tools that are given to us as iOS developers. First let’s look at how NSAttributedStrings are normally used.
In the above, we must create an attributes dictionary which specifies what styles we would like to apply to our text. Next we combine our string and attributes using the `NSAttributedString` initialiser and set it to the `.attributedText` property of the UILabel.
There are a few things I don’t like about this:
- The attributes is a dictionary of type `[String : Any]`. This makes it really easy to make a mistake in putting the wrong type for values as it is of type `Any`.
- We have to remember (or look up the documentation) for all the possible keys that can go in the attributes dictionary.
- Creating the attributed string using the NSAttributedString initialiser is too clunky for me. We can do better.
- Appending attributed strings to one another (useful for multiple styles in once sentence) is really quite difficult.
Lets make this better. The first problem we can solve is converting a string into an NSAttributed String. This is really easy with the use of Swift extensions (this is going to become a common theme). We’ll simply use the NSAttributeString initialiser that takes a plain old string.
This is much better. It is cleaner and just feels right.
Next we want to add styles to our text. I’ve opted to go for a `fluent` API that allows us to chain functions together, adding an attribute to our string with each function. This makes our API very composable and easy to read. To do this we are going to use, you guessed it, Swift extensions.
This look like a bit of a mess, but let me explain:
- We make sure the string of our attributed string is not empty, otherwise we just return `self`.
- We create a mutable version of our attributed string (as you can only apply styles to an existing attributed string if it is of type NSMutableAttributedString).
- we apply the style that we desire for the particular function. In function `medium` we are applying a font with a font size provided by the user of the function. In the function `colored`, we are apply a text colour which is provided by the caller of the function.
- We apply the attribute to our mutable attributed string over the length of the entire string.
Now all we have to do to get the same styling as what we achieved at the beginning of this post is this:
As you can see, this is a much better API that is more concise, readable and composable. The fluent API reads like english and the strong typing will prevent us from making any type errors.
Lets add some paragraph styles using this fluent API which are notoriously difficult to get set up using the API given to us by Apple. Lets allow the user to align attributed strings with this fluent API.
This is similar to before but just a little bit more involved, due to the complexities of adding paragraph styles to attributed text.
- We make sure the string of our attributed string is not empty, otherwise we just return `self`. Same as before.
- We create a mutable version of our attributed string. Same as before.
- We pull out an existing paragraph styles that are attached to this string. This is to allow us to ADD a paragraph style to any existing paragraph styles, rather than OVERWRITING existing styles.
- We get a mutable version of the paragraph styles, or if there are no existing paragraph styles, create a new NSMutableParagraphStyle.
- Set our alignment
- Create the attribute dictionary. Same as before.
- Add the attribute to the length of the string. Same as before
As can be in the example usage, this API makes things much easier when styling strings in iOS. Lets make styling strings fun again.