RichString

The easiest way to work with attributed strings in Swift

Swift Version 4 Percentage Documented Badge CocoaPods Version Badge Supported Platforms Badge Carthage compatible license Build Status

Introduction

This Swift framework was built to simplify working with NSAttributedString. It does so without adding additional types; it just extends the existing types that you already have, ensuring that it is fully interoperable with any other method of using NSAttributedString.

The core assumption this framework makes, is that you set up your attributed strings by first configuring all components appropriately, and then concatenating them.

It allows you to do things like this:

let title = "This is a title".fontSize(17).color(.blue)
let text = "This is some text".paragraphStyle {
    $0.firstLineHeadIndent = 20
}
let content = title + text

Overview

This framework provides the following primitives. There are unit tests; have a look at them for more examples.

Operator + For Concatenating Attributed Strings

Given two strings, you can append one to the other using the + operator:

let s1 = NSAttributedString()
let s2 = NSAttributedString()
let concatenated = s1 + s2

RichString protocol for String, NSString and NSAttributedString

font(_:)

Apply the given font. The type Font is a type alias of UIFont on iOS, and of NSFont on macOS.

let attributedString = "text".font(.systemFont(ofSize: 12))

fontSize(_:)

Applies the given font size. If no font was set on the attributed string yet, Font.systemFont will be assumed.

Note: unlike on iOS, on macOS this returns an optional attributed string instead.

let attributedString = "text".fontSize(12)

paragraphStyle(configure:)

Applies a paragraph style, configuring it with the given closure. If the attributed string already had a paragraph style attribute, the configure closure is called on that paragraph style; otherwise a new NSMutableParagraphStyle is used.

let attributedString = "Hello World".paragraphStyle {
    $0.firstLineHeadIndent = 10
}

paragraphStyle(_:)

Applies the given paragraph style.

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 10
let attributedString = "text".paragraphStyle(paragraphStyle)

color(_:)

Applies the given (foreground) color.

let attributedString = "text".color(.blue)

backgroundColor(_:)

Applies the given background color.

let attributedString = "text".backgroundColor(.yellow)

ligature(_:)

Configures whether or not to use ligatures. Default is that they are used.

let attributedString = "text".ligature(false)

kern(_:)

Configures the amount with which to modify the default kerning. The default 0 means that no kerning change is applied.

let attributedString = "text".kern(0.1)

strikeThrough(style:)

Configures the strike through style. Please note that depending on OS and version not all styles may actually work.

let attributedString = "text".strikeThrough(style: .styleSingle)

strikeThrough(color:)

Configures the strike through color. Only setting the color has no effect, the style must be configured as well.

let attributedString = "text".strikeThrough(color: .red)

strikeThrough(color:, style:)

Configures both the strike through color and style. Please note that depending on OS and version not all styles may actually work.

let attributedString = "text".strikeThrough(color: .red, style: .styleDouble)

underline(style:)

Configures the underline style. Please note that depending on OS and version not all styles may actually work.

let attributedString = "text".underline(style: .styleSingle)

underline(color:)

Configures the underline color. Only setting the color has no effect, the style must be configured as well.

let attributedString = "text".underline(color: .blue)

underline(color:, style:)

Configures both the underline color and style. Please note that depending on OS and version not all styles may actually work.

let attributedString = "text".underline(color: .blue, style: .styleSingle)

stroke(width:, color:)

Configures the stroke.

let attributedString = "text".stroke(width: 2, color: .green)

shadow(configure:)

Configures the shadow using a closure that receives an NSShadow instance. Not available on watchOS.

let result = "Hello World".shadow {
    $0.shadowOffset = CGSize(width: 3, height: 3)
    $0.shadowBlurRadius = 2
    $0.shadowColor = Color.gray
}

shadow(_:)

Configures the shadow by setting an NSShadow instance. Not available on watchOS.

let shadow = NSShadow()
shadow.shadowColor = .green
shadow.shadowBlurRadius = 2
shadow.shadowOffset = 3
let attributedString = "text".shadow(shadow)

letterPressed()

Adds the “letter pressed” text effect.

let attributedString = "text".letterPressed()

Creates hyperlink to the given URL with the receiver as text.

let url = URL(string: "https://example.com")
let attributedString = "text".link(url: url)

Creates hyperlink to the given URL with the receiver as text.

let urlString = "https://example.com"
let attributedString = "text".link(string: urlString)

attachment(configure:)

Creates a new NSTextAttachment and passes it to the configure closure. Not available on watchOS.

let attributedString = NSAttributedString().attachment {
    $0.image = UIImage(...)
    $0.bounds = CGRect(...)
}

baselineOffset(_:)

Configures the baseline offset.

let attributedString = "text".baselineOffset(-0.5)

obliqueness(_:)

Configures the skew to be applied to glyphs.

let attributedString = "text".obliqueness(1.5)

expansion(_:)

Configures the expansion to be applied to glyphs.

let attributedString = "text".expansion(2)

NSAttributedString Extension for Getting Attribute Values

All of the above methods have corresponding getters to retrieve the attribute values from the attributed string. The all return an optional; if the attribute is not configured on the attributed string, nil will be returned.

The getters are:

  • attachment: NSTextAttachment? (not available on watchOS)
  • backgroundColor: Color?
  • baselineOffset: Float?
  • color: Color?
  • expansion: Float?
  • fontSize: CGFloat?
  • isLetterPressed: Bool?
  • kern: Float?
  • ligature: Bool?
  • link: NSURL?
  • obliqueness: Float?
  • paragraphStyle: NSParagraphStyle?
  • shadow: NSShadow? (not available on watchOS)
  • strikeThroughColor: Color?
  • strikeThroughStyle: NSUnderlineStyle?
  • strokeColor: Color?
  • strokeWidth: Float?
  • underlineColor: Color?
  • underlineStyle: NSUnderlineStyle?

Usage

You can use the library any way you like, but two easy ways are Carthage and CocoaPods. For CocoaPods, put pod RichString in your Podfile.

Alternative Frameworks

Back when I started, I found a couple other frameworks that have the same goal as this one: simplifying using NSAttributedString. I like mine better, mostly because it has a simpler API that doesn’t use any additional types. But I’ll let you choose for yourself. I haven’t researched this anymore after that, so this is probably outdated.

TextAttributes is similar to this framework, but it introduces a new type that you have to set up the attributes, instead of working directly on the strings themselves. It doesn’t feature the cool closure-based way of setting up shadow { ... } and paragraphStyle { ... } that this framework has. And it doesn’t provide the super-convenient + operator that this framework does. Finally, I didn’t test this framework on tvOS and watchOS. I’m rather confident that those will work as well, but you’ll have to try. Pull requests are wellcome of course.

TextAttributesUtil also introduces a new type for setting up the attributes. Also, it’s API forces you to have more levels of nesting in your source code. It only supports iOS, not macOS.