Swift Swift: Using Attributed Strings in Swift

[Updated to Swift 2.0/iOS9  9/30/15 SJL]
Sometimes you want to be fancy with your strings. 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

In Xcode open a new project with Command-Shift-N. Use an iOS Application of a Single View Template. Add one label and three buttons looking like this:
Screenshot 2014-10-19 06.59.09
Open the assistant editor, set to Automatic so the ViewController.swift file appears. Control-drag from the label to the code in the ViewController class. Make an outlet called myLabel. Drag from the ChalkDuster button to the code. Make an action named myFontButton. From the circle next to the code click-drag from the circle to the button named Georgia until the button highlights. Release the button, do the same from the code to the button titled AmericanTypewriter-Bold.
Close the assistant editor, and go over to the ViewController.swift file. Remove the didRecieveMemoryWarning method.
The class so far should look like this:

class ViewController: UIViewController {
    @IBOutlet weak var myLabel: UILabel!
    @IBAction func myFontButton(sender: UIButton) {
    }
    override func viewDidLoad() {
        super.viewDidLoad()

    }

Add the following two properties to the ViewController class:

var myString = "I Love Pizza!"
var myMutableString = NSMutableAttributedString()

We will need a string and attributed text string. I’m using a NSMutableAttributedString so I can add attributes as we go along in the lesson. If you have only one time you set attributes, you can do that with a NSAttributedString.

Initializing an Attributed String

in the viewDidLoad method, add the following code:

 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. You will see this in the label:
Screenshot 2014-10-19 07.23.08

Some Things to do with Attributed Text

Once you make the 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 ones for examples.

Add the following below the Add attributes here comment:

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

Just like the initializer, we use the NSFontAttributeName attribute as our attribute and 24 point Chalkduster as our font. 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 7 and the length 5. If you do the counting, this is the word Pizza in our original string. This will change the font of Pizza to 24 point Chalkduster. Build and run

Next add this line under the last one:

myMutableString.addAttribute(NSFontAttributeName,
    value: UIFont(
        name: "AmericanTypewriter-Bold",
        size: 18.0)!, 
    range: NSRange(
        location:2,
        length:4))

This time we change the font of the word Love to 18pt American Typewriter bold. Build and run:

Now add this under the previous two lines:

myMutableString.addAttribute(NSForegroundColorAttributeName,
    value: UIColor.redColor(), 
    range: NSRange(
        location:2,
        length:4))

We change the font color to red on the word Love with the NSForegroundColorAttributeName attribute . Build and run:

And now add these three lines under those:

myMutableString.addAttribute(NSFontAttributeName,
    value: UIFont(
        name: "Georgia",
        size: 36.0)!,
    range: NSRange(
        location: 0, 
        length: 1))
myMutableString.addAttribute(NSStrokeColorAttributeName,
    value: UIColor.blueColor(), 
    range:  NSRange(
        location: 0,
        length: 1))
myMutableString.addAttribute(NSStrokeWidthAttributeName,
    value: 2, 
    range: NSRange(
        location: 0,
        length: 1))

The first attribute we know already. We set 36pt Georgia to the word I. Line two using NSStrokeColorAttributeName sets the color for the outline of the letter I to blue. The third attribute uses NSStrokeWidthAttributeName to set a stroke width, giving us an outlined letter. Build and run this:

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

let stringLength = NSString(string: myString).length
myMutableString.addAttribute(NSBackgroundColorAttributeName,
    value: UIColor.greenColor(),
    range: NSRange(
        location: 0, 
        length: stringLength))
myLabel.backgroundColor = UIColor.grayColor()

Our range this time will be the complete string. The first line gets the length of the string is a slightly odd way. String has no length property Where NSString does. The last line is for better contrast of what is going to happen. The NSBackgroundColorAttributeName attribute will change the background for the font to green, but it does not change the background for the view. Our label is gray, and our font background is green. Build and run:

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.

Changing the Same Range

Nothing says you cannot change a range once you added it. Change the myFontButton to

 @IBAction func myFontButton(sender: UIButton) {
    myMutableString.addAttribute(NSFontAttributeName,
        value: UIFont(
            name: sender.titleLabel!.text!, 
            size: 24.0)!, 
        range: NSRange(
            location: 7,
            length: 5))
   myLabel.attributedText = myMutableString
    }

In this code we take the title of the button and use it as a font name for the word Pizza. Build and run. Tap the Georgia button,

tap the American Typewriter Bold Button

Tap the Chalkduster Button.

Screenshot 2014-10-19 07.42.28

This is just the start of what you can do with attributed text. In the next lesson we’ll apply it to more labels and the UIPickerView — Sort of.

The Whole Code

//
//  ViewController.swift
//  SwiftAttributedString
//
//  Created by Steven Lipton on 10/18/14.
//  Copyright (c) 2014 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var myLabel: UILabel!
    @IBAction func myFontButton(sender: UIButton) {
        myMutableString.addAttribute(NSFontAttributeName,
            value: UIFont(
                name: sender.titleLabel!.text!,
                size: 24.0)!,
            range: NSRange(
                location: 7,
                length: 5))
        myLabel.attributedText = myMutableString
    }
    var myString = "I Love Pizza!"
   
    var myMutableString = NSMutableAttributedString()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Initialize the mutable string
        myMutableString = NSMutableAttributedString(
            string: myString,
            attributes: [NSFontAttributeName:
                UIFont(name: "Georgia", size: 18.0)!])
        
        //Add more attributes here:
        myMutableString.addAttribute(NSFontAttributeName,
            value: UIFont(
                name: "Chalkduster", 
                size: 24.0)!,
            range: NSRange(
                location: 7,
                length: 5))
        myMutableString.addAttribute(NSFontAttributeName,
            value: UIFont(
                name: "AmericanTypewriter-Bold",
                size: 18.0)!,
            range: NSRange(
                location:2,
                length:4))
        myMutableString.addAttribute(NSForegroundColorAttributeName,
            value: UIColor.redColor(),
            range: NSRange(
                location:2,
                length:4))
        
        myMutableString.addAttribute(NSFontAttributeName,
            value: UIFont(
                name: "Georgia", 
                size: 36.0)!,
            range: NSRange(
                location: 0, 
                length: 1))
        myMutableString.addAttribute(NSStrokeColorAttributeName,
            value: UIColor.blueColor(),
            range:  NSRange(
                location: 0, 
                length: 1))
        myMutableString.addAttribute(NSStrokeWidthAttributeName,
            value: 2,
            range: NSRange(
                location: 0, 
                length: 1))
        let stringLength = NSString(string: myString).length
        myMutableString.addAttribute(NSBackgroundColorAttributeName,
            value: UIColor.greenColor(),
            range: NSRange(
                location: 0, 
                length: stringLength ))
        myLabel.backgroundColor = UIColor.grayColor()
        
        //Apply to the label
        myLabel.attributedText = myMutableString
    }

}


13 thoughts on “Swift Swift: Using Attributed Strings in Swift”

  1. Thanks. Your posts and sample codes help me a lot. Does the range can also work in a relative way? I have a problem which I don’t know how to solve. My text in my label consist of two parts, “elem1 elem2.” I want to make the elem1 bold and elem. regular text, but the length of elem1 is not a fixed length. How can I define the range to solve this problem?

    1. You make elem1’s range starting at position 0 and its length the length of elem1. Then you make the position of elem2 the length of elem 1 and sinc e Ithink there’s a space in there, you make provisions for the space by adding 1. Then that the length of your elem2. (I’m not 100% sure of syntax, I’m doing this off the top of my head)

      The general case is

      var elem1 = someOtherString
      let elem2 = "fixed string"
      let myString = elem1 + " " + elem2 
      let elem1Range = NSRange(location: 0, length: elem1.length)
      let elem2Range = NSRange(location: elem1.length + 1, length:elem2.length)
      

      Probably would be best to make everything the elem2 text first, then set attribute for elem1, so that would reduce to

      var elem1 = someOtherString
      let elem2 = "fixed string"
      let myString = elem1 + " " + elem2 
      myMutableString = NSMutableAttributedString(string: myString, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 18.0)!])
      myMutableString.addAttribute(NSFontAttributeName, value: UIFont(name: "Georgia-Bold", size: 18.0)!, range:NSRange(location: 0, length: elem1.length) )
      

      If not right, it’s close. I don’t have everywhere to check it right now. But you then can use those ranges for your attributed text.

  2. Your article is great and explains everything well. One thing is that I am looking for a way to put a line above a character in a UILabel. Is there a way of doing that?

  3. Is it possible to change color of a string depending on an percentage, i.e. change 21.8% of white text string to blue? The reason I ask is that I have a white text label on top of a blue bar on a white background. However, if the length of the blue bar is less than the length of the label, part of the white text is no longer visible and I would like to change the color.

    1. No, not that I am aware of. If you wanted to do a bit of work, you might be able to take the width of the bar and compute what the range of characters under it was. You might want to not use attributed strings and look at some of the Quartz methods for applying text, which might give you a bit more power.

  4. Hi! Is it possible to change the color of one Sklabel with an action?

    I mean, I have a matrix of SKlabel, but everyone with the same name (I painted with a for) so the idea its change one by one. Is it that possible to do with an action?

    1. All SKNodes have a name property. Name all your nodes in some way during creation in the for loop with a unique identifier, and set the name property to that unique identifier, in your method or action, change that named node.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s