Using NSAttributedString for string styling in iOS iOS 15.04.2020

NSAttributedString is a Foundation class used for more complicated string styling within iOS. You can change things like color, font, size, kerning, etc on specific ranges within a string using NSAttributedString. Attributes for an NSAttributedString are set using a dictionary, and the iOS SDK provides a number of keys you can use for that dictionary such as NSAttributedString.Key.foregroundColor.

NSAttributedString allows you to customize the text attributes of UILabel, UIButton, UIPickerView, and UITableViewCell.

Let’s look at a simple example

let attributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.blue]
let attributedString = NSAttributedString(string: "Swift here", attributes: attributes)
myLabel.attributedText = attributedString

Here we’re creating our attributes dictionary with a foreground color of blue, then creating an attributed string with those attributes. Finally, we’re setting the text on a UILabel to the new attributed string.

Say we want the word "Swift" to be one font, and the word "here" to be a different font.

let swiftAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.boldSystemFont(ofSize: 16)]
let hereAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Avenir-Light", size: 14.0)]

let swiftString = NSMutableAttributedString(string: "Swift", attributes: helloAttributes)
let hereString = NSAttributedString(string: "here", attributes: worldAttributes)
swiftString.append(hereString)

myLabel.attributedText = swiftString

If you’ve ever worked with NSRange before then you can simplify what’s happening above a little bit like this:

let swiftAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Bold", size: 20.0)]
let hereAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont(name: "Avenir-Light", size: 14.0)]

let str = NSMutableAttributedString(string: "Swift here", attributes: swiftAttributes)
str.addAttributes(hereAttributes, range: NSRange(location: 6, length: 4))

myLabel.attributedText = str

Ranges can be really helpful when you’re manipulating a string.

let swiftString = "Swift is the best language ever!"
let boldAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Bold", size: 20.0)]
let range = NSString(string: swiftString).range(of: "best")

let swiftAttributedString = NSMutableAttributedString(string: swiftString)
swiftAttributedString.addAttributes(boldAttributes, range: range)

myLabel.attributedText = swiftAttributedString

There are lots of formatting options for attributed strings, including:

  • Set .underlineStyle to a value from NSUnderlineStyle to strike out characters.
  • Set .strikethroughStyle to a value from NSUnderlineStyle to strike out characters.
  • Set .paragraphStyle to an instance of NSMutableParagraphStyle to control text alignment and spacing.
  • Set .link to be a URL to make clickable links in your strings.

Now lets apply different color to some characters by adding another attribute just after the first addAttribute line

let string = "https://en.proft.me"

let attributedString = NSMutableAttributedString(string: string)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.blue, range: NSRange(location: 7, length: 11))

Add another attribute to the attributed string just after the last addAttribute line

attributedString.addAttribute(NSStrokeColorAttributeName, value: UIColor.red, range: NSRange(location: 7, length: 11))

Make some characters bold using NSAttributedString

attributedString.addAttribute(NSFontAttributeName, value: UIFont(name: "AmericanTypewriter-Bold", size: 24)!, range: NSRange(location: 7, length: 11))

We can use NSUnderlineColorAttributeName and NSUnderlineStyleAttributeName to define underline color and style for our string

attributedString.addAttribute(NSUnderlineColorAttributeName, value: UIColor.blue, range: NSRange(location: 0, length: 18))
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: 1, range: NSRange(location: 0, length: 18))

HTML as NSAttributedString

Converting HTML String to NSAttributedString can be easily accomplished with this String extension:

extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.data(using: .utf8) else {
            return nil
        }

        return try? NSAttributedString(
            data: data,
            options: [.documentType: NSAttributedString.DocumentType.html],
            documentAttributes: nil
        )
    }
}

To use,

label.attributedText = "<b>Hello</b> \u{2022} World".htmlAttributedString()

Justify Text

let sampleText = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

// Create label
let label = UILabel(frame: CGRectMake(0, 0, view.frame.size.width, 400))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping

// Justify text through paragraph style
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = NSTextAlignment.Justified
let attributes = [NSParagraphStyleAttributeName: paragraphStyle, NSBaselineOffsetAttributeName: NSNumber(float: 0)]
let attributedString = NSAttributedString(string: sampleText, attributes: attributes)
label.attributedText = attributedString
view.addSubview(label)

Custom kerning (letter spacing)

var attributedString = NSMutableAttributedString("Apply kerning")
attributedString.addAttribute(attribute: NSKernAttributeName, value: 5, range: NSMakeRange(6, 7))
label.attributedText = attributedString

Line and paragraph spacing

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.5
let attributes = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body), 
    NSAttributedString.Key.paragraphStyle: paragraphStyle,
    NSAttributedString.Key.foregroundColor: UIColor.label
]

textView.attributedText = NSAttributedString(string: sampleText, attributes: attributes)

Useful links