Make App Pie

Training for Developers and Artists

Using Attributed Strings in Swift 3.0

For most uses, a basic string works well. Normal strings have no character or paragraph formatting. They rely on the properties of the control to format them. The system only formats the full string, not parts of it. For formatting a part of a string, you need an attributed string which specifies parts of the string formatted in different ways. In this lesson we’ll learn how to work in attributed strings in Swift.

Set Up A Project

For this project, you can use a playground on Xcode or on iPad or add the code as an Xcode project. Let’s look at the setup for each.

Starting  a Xcode App Project

In Xcode use the Command-Shift-N keyboard shortcut to open a new project Swift3AttributedString. Use an iOS Application of a Single View Template.

Go to the ViewController.Swift file, and clean out the ViewController class to look like this:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

    }

 

Starting a Xcode Playground

In Xcode,  Press Option-Command-Shift-N to get a new playground. Use an iOS playground named Swift3AttributedString.

2016-07-02_09-22-46

Starting a iPad Playground

Open the iPad application. If you have  playgrounds displayed, press the browser button on the upper right.

2016-07-02_09-07-26

Click either Add ( + ) button in the browser.

IMG_0388

If you select the one in the upper left, you’ll be asked if you want to load a file from an external source as well. Select Create playground

IMG_0389

For either Create Playground button, you’ll be asked for which template to use. Select Blank

IMG_0390

Code Setup for Playgrounds

Set up your playground code like this:

import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

    }
//Uncomment these lines if using Playgrounds 
let vc = ViewController()
PlaygroundPage.current.liveView = vc
PlaygroundPage.current.needsIndefiniteExecution = true

We’ve created the same empty ViewController class as the app.  At first, you might see Xcode complaining about a syntax error: just ignore it for now. For the iPad version, you’ll see the contents in an automatically opening window. If you are in Xcode, open the assistant editor set to Timeline.

Setting Up the View Controller

For this lesson, we only need one label, so to be compatible on all devices we’ll set it up programmatically with a quick and dirty way.  Go over to the ViewController class. Change viewDidLoad to the following to add a label:

override func viewDidLoad() {
   super.viewDidLoad()
   // declarations     
   let myLabel = UILabel()
   let myString = "P is for Pizza and Pizza is for me"
   //set the view background color     
   view.backgroundColor = UIColor.white()        
   //set up the label
   myLabel.frame = view.bounds.insetBy(dx: 20, dy: 20)
   //myLabel.frame.size.width = 350.0 //Uncomment for playgrounds
   myLabel.lineBreakMode = .byWordWrapping
   myLabel.numberOfLines = 0 //Variable number of lines
   myLabel.backgroundColor = UIColor.yellow()
   myLabel.text = myString
   view.addSubview(myLabel)

Build and run. You’ll get  something like this:

2016-07-03_15-19-39

Here we used the text property of UILabel, giving us our plain old label.  We left a margin of white background  for some thing that will happen later. Let’s now jazz this up.

Making a Mutable String

We will need an  attributed text string. There are two varieties NSMutableAttributedString and AttributedString. If you are wondering why one has a NS in front and the other doesn’t so am I. Since I’m using Beta 1 of Xcode 8, Apple is still working on removing all the NS‘s they promised to remove. If your code doesn’t work because it can’t find something with an NS prefix, try removing the NS.

I’m using a NSMutableAttributedString so I can add attributes as we go along in the lesson. If you set attributes only once, you can do that with AttributedString. We’ll look at one later.

In the viewDidLoad method after the label setup, add the following code:

let myMutableString = NSMutableAttributedString(
    string: myString,
    attributes: [NSFontAttributeName:UIFont(
        name: "Georgia",
        size: 18.0)!])
//Add more attributes here

//Apply to the label
myLabel.attributedText = myMutableString

Line 1 initializes an attributed string with the string myString and an attribute dictionary. Here we used an attribute named NSFontAttributeName as the key, and a UIFont object as the value. Build and run using the default iPhone SE . You will see this in the label:

2016-07-01_06-16-43

An Attributed Text Sampler

Once you make the mutable attributed string you can change it as much as you want with the addAttribute method. There are a lot of attributes to change. For a list of them, you can look here in Apple’s documentation. I’ll go through a few useful or fun ones for examples.

Changing Fonts

Add the following below the Add attributes here comment:

myMutableString.addAttribute(NSFontAttributeName,
    value: UIFont(
        name: "Chalkduster",
        size: 24.0)!,
    range: NSRange(
        location: 9,
        length: 5))

We use the NSFontAttributeName attribute as our attribute and 24 point Chalkduster as our font.  That is the same as the initializer. What is new is range. An NSRange is a struct used for sequential data, such as strings. It has a location and a length. In the case above the location is 9 and the length 5. If you do the counting, this is the first Pizza in our original string. This will change the font of Pizza to 24 point Chalkduster. Build and run

2016-07-01_06-21-10

Changing Text Color

Add these attributes under the last one:

myMutableString.addAttribute(NSFontAttributeName,
    //: Make a big blue P
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "AmericanTypewriter-Bold",
                size: 36.0)!,
            range: NSRange(
                location:0,
                length:1))
        myMutableString.addAttribute(
            NSForegroundColorAttributeName,
            value: UIColor.blue(),
            range: NSRange(
                location:0,
                length:1))

This time we changed two attributes. First we set the the font of the P to 20pt American Typewriter bold. We also charged the color the the P to blue with the NSForegroundColorAttributeName  attribute. Build and run:

2016-07-03_12-42-58

Making Outline Fonts

Add these three attributes under those:

//: Make the second pizza red and outlined in Helvetica Neue
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Helvetica Neue",
                size: 36.0)!,
            range: NSRange(
                location: 19,
                length: 5))
        myMutableString.addAttribute(
            NSStrokeColorAttributeName,
            value: UIColor.red(),
            range:  NSRange(
                location: 19,
                length: 5))
        
        myMutableString.addAttribute(
            NSStrokeWidthAttributeName,
            value: 4,
            range: NSRange(
                location: 19,
                length: 5))

The first attribute we know already. We set 36pt Helvetica Neue to the second Pizza. Then NSStrokeColorAttributeName sets the color for the outline of Pizza to Red. The third attribute uses NSStrokeWidthAttributeName to set a stroke width of 4 points, giving us an outlined letter. Build and run this:

2016-07-03_12-51-01

Character Background Change

You can set the background as well, but be careful. Add this to the code under the last code:

//: Set the background color is attributes text. 
//: which is not the color of the background text.
let  stringLength = myString.characters.count
myMutableString.addAttribute(
    NSBackgroundColorAttributeName,
    value: UIColor.magenta(),
    range: NSRange(
        location: 0,
        length: stringLength))
        

Our range this time will be the complete string. The first line gets the length of the string using characters.count. The NSBackgroundColorAttributeName attribute will change the background for the characters to magenta, but it does not change the background for the label view. Our label is still yellow, but our font background is magenta. Build and run:

2016-07-03_13-07-13

The font background will be the height of the largest letter, and as long as the range specified. If the label is bigger than the text size, you will have two background colors.

Adding a Drop Shadow to Text

You can also add drop shadows to text. Before adding the drop shadow you need to define a drop shadow object

 //: Add a Drop Shadow
        
        //: Make the Drop Shadow
        let shadow = NSShadow()
        shadow.shadowOffset = CGSize(width: 5, height: 5)
        shadow.shadowBlurRadius = 5
        shadow.shadowColor = UIColor.gray()

We’ve set a offset of 5 points by 5 points, a blur radius of 5 and a gray shadow. Now we can use the NSShadowAttributeName to make a shadow, using shadow for the value, and change the font to 48 point Menlo.

        
        //: Add a drop shadow to the text
        myMutableString.addAttribute(
            NSShadowAttributeName,
            value: shadow,
            range: NSRange(
                location: 27,
                length: 7))
        
        //:Change to 48 point Menlo
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Menlo",
                size: 48.0)!,
            range: NSRange(
                location: 27,
                length: 7))

Build and run.

2016-07-03_16-05-12

Appending Text

Suppose you wanted to add three exclamation marks to the end of our sentence. That requires this code:

let myAddedString = AttributedString(string: "!!!", attributes: nil)
myMutableString.append(myAddedString)

Build and run:

2016-07-03_15-38-09

The attribute  argument above were set to nil. When using nil, the attributes are the initialized attributes. If we want to have different attributes, we define them as a dictionary of [String:AnyObject]?. The string would be the AttributeName, and the value the value: argument we used in the previous examples. For example, let’s make the exclamation point be AvenirNext-Heavy at 48 point with the drop shadow we defined earlier. Add this just above the myAddedString definition.

//: appending the String with !!! as an Attributed String
let myAddedStringAttributes:[String:AnyObject]? = [
    NSFontAttributeName:UIFont(
        name: "AvenirNext-Heavy",
        size: 48.0)!,
    NSForegroundColorAttributeName:UIColor.red(),
    NSShadowAttributeName: shadow
            
]

Change the nil to myAddedStringAttributes

let myAddedString = AttributedString(
    string: "!!!",
    attributes: myAddedStringAttributes)
myMutableString.append(myAddedString)

Build and run

2016-07-03_15-39-53

This is just the start of what you can do with attributed text.

The Whole Code

This code is compatible with both Projects and Playgrounds. For it to work with playgrounds, uncomment the lines instructing you to do so.


//  ViewController.swift
//  SwiftAttributedString
//
//  Created by Steven Lipton on 10/18/14.
//  Revised Swift 3.0 7/1/16
//  Copyright (c) 2014,2016 MakeAppPie.Com. All rights reserved.
//

import UIKit
//import PlaygroundSupport  //uncomment for Playgrounds
class ViewController: UIViewController {
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myLabel = UILabel()
        let myString = "P is for Pizza and Pizza is for me"
        
        view.backgroundColor = UIColor.white()
        //initialize the label to be the entire view
        //and to word wrap
        
        myLabel.frame = view.bounds.insetBy(dx: 20, dy: 20)
        //mylabel.frame.size.width = 350.0//uncomment for playgrounds
        myLabel.lineBreakMode = .byWordWrapping
        myLabel.numberOfLines = 0
        myLabel.backgroundColor = UIColor.yellow()
        myLabel.text = myString
        view.addSubview(myLabel)

        //: Initialize the mutable string
        let myMutableString = NSMutableAttributedString(
            string: myString,
            attributes: [NSFontAttributeName:
                UIFont(name: "Georgia", size: 18.0)!])
       
        //: Add more attributes here:
        //: Make the first Pizza in chalkduster 24 point
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Chalkduster",
                size: 24.0)!,
            range: NSRange(
                location: 9,
                length: 5))
        
        //: Make a big blue P
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "AmericanTypewriter-Bold",
                size: 36.0)!,
            range: NSRange(
                location:0,
                length:1))
        myMutableString.addAttribute(
            NSForegroundColorAttributeName,
            value: UIColor.blue(),
            range: NSRange(
                location:0,
                length:1))
        
        
        //: Make the second pizza red and outlined in Helvetica Neue
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Helvetica Neue",
                size: 36.0)!,
            range: NSRange(
                location: 19,
                length: 5))
        
        myMutableString.addAttribute(
            NSStrokeColorAttributeName,
            value: UIColor.red(),
            range:  NSRange(
                location: 19,
                length: 5))
        
        myMutableString.addAttribute(
            NSStrokeWidthAttributeName,
            value: 4,
            range: NSRange(
                location: 19,
                length: 5))
        
        //: Set the background color is attributes text. 
        //: which is not the color of the background text.
        let  stringLength = myString.characters.count
        myMutableString.addAttribute(NSBackgroundColorAttributeName,
                                     value: UIColor.magenta(),
                                     range: NSRange(
                                        location: 0,
                                        length: stringLength))
        
        
        
        //: Add a Drop Shadow
        
        //: Make the Drop Shadow
        let shadow = NSShadow()
        shadow.shadowOffset = CGSize(width: 5, height: 5)
        shadow.shadowBlurRadius = 5
        shadow.shadowColor = UIColor.gray()
        
        //: Add a drop shadow to the text
        myMutableString.addAttribute(
            NSShadowAttributeName,
            value: shadow,
            range: NSRange(
                location: 27,
                length: 7))
  
        //:Change to 48 point Menlo
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Menlo",
                size: 48.0)!,
            range: NSRange(
                location: 27,
                length: 7))
        
        //: Appending the String with !!! and an Attributed String
        let myAddedStringAttributes:[String:AnyObject]? = [
            NSFontAttributeName:UIFont(
                name: "AvenirNext-Heavy",
                size: 48.0)!,
            NSForegroundColorAttributeName:UIColor.red(),
            NSShadowAttributeName: shadow
            
        ]
        let myAddedString = AttributedString(
            string: "!!!",
            attributes: myAddedStringAttributes)
        myMutableString.append(myAddedString)

        //: Apply to the label
        myLabel.attributedText = myMutableString
       
    }
    
}

//Uncomment this for use in playgrounds.
/*
 let vc = ViewController()
 PlaygroundPage.current.liveView = vc
 PlaygroundPage.current.needsIndefiniteExecution = true
 */

20 responses to “Using Attributed Strings in Swift 3.0”

  1. Hi Steven, thanks for the tutorial! I was planning on using NSAttributedStrings for an app I was working on but there was one thing I couldn’t figure out… How can I get the attributes to save to the specific string? For further reference my app will be displaying txt files into a UITextView. I want to give give users the ability to highlight specific portions of the text for future reference. I had figured out how to allow users to highlight pieces of the text using the BackgroundColorAttribute but when they leave and return the attributes have disappeared. I’m fairly new to IOS development but what I am looking into is using NSUserDefaults, writing to the txt file, or possibly something with Core Data? This is a little off topic but any help is appreciated!

    1. Core data would be overkill, and NSUserdefaults might be a too weak, though you do want a persistent data structure. Attributed strings at their core are the dictionary entries [String:AnyObject] and and NSRange the where to apply them. If you are only doing background colors, Make a struct or class to the color (or rather three components of the color RGB,HSV) and the range data and store them in an array, which converts this data either to a CSV file or XML file. Read back the file when necessary.

      1. Sweet! Thanks Steven, I will give that a try. Also I had another question… In this app that I am building I am trying to set up almost a chapter like sort through of documents. So for example you would have this first table view which shows the broad document names but then clicking on that name would take you to a tableview with the chapters for that document then clicking on a chapter would take you to a table with the subchapters. I had originally planned to try to use a CSV file and import it into Core Data but I read another one of your posts which said CSV files don’t work very well for dictionary data sets which is what I would probably need to do. Would you recommend using a plist file or is that bad coding practice?

      2. Yes, CSV is too linear for what you need. XML or JSON would work. Core data would too, but I find that more work than its worth for such projects.

      3. As for plists, try to keep them small. They aren’t built for big chunks of data.

  2. Hi Stevem, thanks for the post, very helpful. Do you know if there’s a way of seing the resulting text in the main storyboard?

    1. I don’t know of one. Attributed text is dynamic and set at runtime, so I doubt there is. I

  3. FYI: There’s a missing } in the initial playground code (to close off the ViewController class)

    1. I’ll have to fix it. Thanks!!!

      1. You’re welcome. Also, this line at the start of the “Changing Text Color” section of code is out-of-place: “myMutableString.addAttribute(NSFontAttributeName,”

  4. thanks! oh, and in my playground got an error with line 137.
    let myAddedString = AttributedString(
    should be
    let myAddedString = NSAttributedString(

    1. must have been changed again from when I wrote it.

  5. Hello How to Use this Using TextView?

  6. Hi Steven, thanks for the great tutorial. I have one question. Is there a way to make the frame of backgroundColor of some text a bit bigger, since this default one cuts it pretty close to letters and i wanted to give them a bit more space?

    Thanks in advance
    Dusan

    1. Not that I know of without using a UIView for your “padding” and a textView or label as a subview. You can do something like that with Stackviews too.

  7. Hi Steven, thanks for the great tutorial. I have one question. Is there a way to make the frame of backgroundColor of some text a bit bigger, since this default one cuts it pretty close to letters and i wanted to give them a bit more space?

    Thanks in advance
    Dusan

    1. Not that I know of without using a UIView for your “padding” and a textView or label as a subview. You can do something like that with Stackviews too.

  8. This is a great tutorial. I’m trying to apply this kind of formating to a UITextField. I have a social media-type web app that I’m porting to iOS. We are trying to implement mentions and hashtag features. I.e. when a user types @ or #, they are presented with a sheet or some searchable list view where they narrow down to the user they want to mention, then on selection, the @ or # is replaced with a link to that user’s profile (mention) or a list of content associated with their tag (hashtags). Can I apply this type of formatting to the UITextFiled’s text (i..e inside the text field)?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: