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.
Starting a iPad Playground
Open the iPad application. If you have playgrounds displayed, press the browser button on the upper right.
Click either Add ( + ) button in the browser.
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
For either Create Playground button, you’ll be asked for which template to use. Select Blank
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:
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:
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
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:
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:
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:
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.
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:
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
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 */
Leave a Reply