I am trying to make a simple Coffee Calculator. I need to display the amount of coffee in grams. The \"g\" symbol for grams needs to be attached to my UILabel that I am usin
Swift 2.0
Here is a sample:
let newsString: NSMutableAttributedString = NSMutableAttributedString(string: "Tap here to read the latest Football News.")
newsString.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleDouble.rawValue], range: NSMakeRange(4, 4))
sampleLabel.attributedText = newsString.copy() as? NSAttributedString
Swift 5.x
let newsString: NSMutableAttributedString = NSMutableAttributedString(string: "Tap here to read the latest Football News.")
newsString.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.double.rawValue], range: NSMakeRange(4, 4))
sampleLabel.attributedText = newsString.copy() as? NSAttributedString
OR
let stringAttributes = [
NSFontAttributeName : UIFont(name: "Helvetica Neue", size: 17.0)!,
NSUnderlineStyleAttributeName : 1,
NSForegroundColorAttributeName : UIColor.orangeColor(),
NSTextEffectAttributeName : NSTextEffectLetterpressStyle,
NSStrokeWidthAttributeName : 2.0]
let atrributedString = NSAttributedString(string: "Sample String: Attributed", attributes: stringAttributes)
sampleLabel.attributedText = atrributedString
Swift 2.1 - Xcode 7
let labelFont = UIFont(name: "HelveticaNeue-Bold", size: 18)
let attributes :[String:AnyObject] = [NSFontAttributeName : labelFont!]
let attrString = NSAttributedString(string:"foo", attributes: attributes)
myLabel.attributedText = attrString
protocol AttributedStringComponent {
var text: String { get }
func getAttributes() -> [NSAttributedString.Key: Any]?
}
// MARK: String extensions
extension String: AttributedStringComponent {
var text: String { self }
func getAttributes() -> [NSAttributedString.Key: Any]? { return nil }
}
extension String {
func toAttributed(with attributes: [NSAttributedString.Key: Any]?) -> NSAttributedString {
.init(string: self, attributes: attributes)
}
}
// MARK: NSAttributedString extensions
extension NSAttributedString: AttributedStringComponent {
var text: String { string }
func getAttributes() -> [Key: Any]? {
if string.isEmpty { return nil }
var range = NSRange(location: 0, length: string.count)
return attributes(at: 0, effectiveRange: &range)
}
}
extension NSAttributedString {
convenience init?(from attributedStringComponents: [AttributedStringComponent],
defaultAttributes: [NSAttributedString.Key: Any],
joinedSeparator: String = " ") {
switch attributedStringComponents.count {
case 0: return nil
default:
var joinedString = ""
typealias SttributedStringComponentDescriptor = ([NSAttributedString.Key: Any], NSRange)
let sttributedStringComponents = attributedStringComponents.enumerated().flatMap { (index, component) -> [SttributedStringComponentDescriptor] in
var components = [SttributedStringComponentDescriptor]()
if index != 0 {
components.append((defaultAttributes,
NSRange(location: joinedString.count, length: joinedSeparator.count)))
joinedString += joinedSeparator
}
components.append((component.getAttributes() ?? defaultAttributes,
NSRange(location: joinedString.count, length: component.text.count)))
joinedString += component.text
return components
}
let attributedString = NSMutableAttributedString(string: joinedString)
sttributedStringComponents.forEach { attributedString.addAttributes($0, range: $1) }
self.init(attributedString: attributedString)
}
}
}
let defaultAttributes = [
.font: UIFont.systemFont(ofSize: 16, weight: .regular),
.foregroundColor: UIColor.blue
] as [NSAttributedString.Key : Any]
let marketingAttributes = [
.font: UIFont.systemFont(ofSize: 20.0, weight: .bold),
.foregroundColor: UIColor.black
] as [NSAttributedString.Key : Any]
let attributedStringComponents = [
"pay for",
NSAttributedString(string: "one",
attributes: marketingAttributes),
"and get",
"three!\n".toAttributed(with: marketingAttributes),
"Only today!".toAttributed(with: [
.font: UIFont.systemFont(ofSize: 16.0, weight: .bold),
.foregroundColor: UIColor.red
])
] as [AttributedStringComponent]
let attributedText = NSAttributedString(from: attributedStringComponents, defaultAttributes: defaultAttributes)
do not forget to paste the solution code here
import UIKit
class ViewController: UIViewController {
private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: .init(x: 40, y: 40, width: 300, height: 80))
label.numberOfLines = 2
view.addSubview(label)
self.label = label
let defaultAttributes = [
.font: UIFont.systemFont(ofSize: 16, weight: .regular),
.foregroundColor: UIColor.blue
] as [NSAttributedString.Key : Any]
let marketingAttributes = [
.font: UIFont.systemFont(ofSize: 20.0, weight: .bold),
.foregroundColor: UIColor.black
] as [NSAttributedString.Key : Any]
let attributedStringComponents = [
"pay for",
NSAttributedString(string: "one",
attributes: marketingAttributes),
"and get",
"three!\n".toAttributed(with: marketingAttributes),
"Only today!".toAttributed(with: [
.font: UIFont.systemFont(ofSize: 16.0, weight: .bold),
.foregroundColor: UIColor.red
])
] as [AttributedStringComponent]
label.attributedText = NSAttributedString(from: attributedStringComponents, defaultAttributes: defaultAttributes)
label.textAlignment = .center
}
}
Please consider using Prestyler
import Prestyler
...
Prestyle.defineRule("$", UIColor.red)
label.attributedText = "\(calculatedCoffee) $g$".prestyled()
Swift 4:
let attributes = [NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Bold", size: 17)!,
NSAttributedStringKey.foregroundColor: UIColor.white]
This answer has been updated for Swift 4.2.
The general form for making and setting an attributed string is like this. You can find other common options below.
// create attributed string
let myString = "Swift Attributed String"
let myAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.blue ]
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
// set attributed text on a UILabel
myLabel.attributedText = myAttrString
let myAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.blue ]
let myAttribute = [ NSAttributedString.Key.backgroundColor: UIColor.yellow ]
let myAttribute = [ NSAttributedString.Key.font: UIFont(name: "Chalkduster", size: 18.0)! ]
let myAttribute = [ NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue ]
let myShadow = NSShadow()
myShadow.shadowBlurRadius = 3
myShadow.shadowOffset = CGSize(width: 3, height: 3)
myShadow.shadowColor = UIColor.gray
let myAttribute = [ NSAttributedString.Key.shadow: myShadow ]
The rest of this post gives more detail for those who are interested.
String attributes are just a dictionary in the form of [NSAttributedString.Key: Any]
, where NSAttributedString.Key
is the key name of the attribute and Any
is the value of some Type. The value could be a font, a color, an integer, or something else. There are many standard attributes in Swift that have already been predefined. For example:
NSAttributedString.Key.font
, value: a UIFont
NSAttributedString.Key.foregroundColor
, value: a UIColor
NSAttributedString.Key.link
, value: an NSURL
or NSString
There are many others. See this link for more. You can even make your own custom attributes like:
key name: NSAttributedString.Key.myName
, value: some Type.
if you make an extension:
extension NSAttributedString.Key {
static let myName = NSAttributedString.Key(rawValue: "myCustomAttributeKey")
}
You can declare attributes just like declaring any other dictionary.
// single attributes declared one at a time
let singleAttribute1 = [ NSAttributedString.Key.foregroundColor: UIColor.green ]
let singleAttribute2 = [ NSAttributedString.Key.backgroundColor: UIColor.yellow ]
let singleAttribute3 = [ NSAttributedString.Key.underlineStyle: NSUnderlineStyle.double.rawValue ]
// multiple attributes declared at once
let multipleAttributes: [NSAttributedString.Key : Any] = [
NSAttributedString.Key.foregroundColor: UIColor.green,
NSAttributedString.Key.backgroundColor: UIColor.yellow,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.double.rawValue ]
// custom attribute
let customAttribute = [ NSAttributedString.Key.myName: "Some value" ]
Note the rawValue
that was needed for the underline style value.
Because attributes are just Dictionaries, you can also create them by making an empty Dictionary and then adding key-value pairs to it. If the value will contain multiple types, then you have to use Any as the type. Here is the multipleAttributes
example from above, recreated in this fashion:
var multipleAttributes = [NSAttributedString.Key : Any]()
multipleAttributes[NSAttributedString.Key.foregroundColor] = UIColor.green
multipleAttributes[NSAttributedString.Key.backgroundColor] = UIColor.yellow
multipleAttributes[NSAttributedString.Key.underlineStyle] = NSUnderlineStyle.double.rawValue
Now that you understand attributes, you can make attributed strings.
Initialization
There are a few ways to create attributed strings. If you just need a read-only string you can use NSAttributedString
. Here are some ways to initialize it:
// Initialize with a string only
let attrString1 = NSAttributedString(string: "Hello.")
// Initialize with a string and inline attribute(s)
let attrString2 = NSAttributedString(string: "Hello.", attributes: [NSAttributedString.Key.myName: "A value"])
// Initialize with a string and separately declared attribute(s)
let myAttributes1 = [ NSAttributedString.Key.foregroundColor: UIColor.green ]
let attrString3 = NSAttributedString(string: "Hello.", attributes: myAttributes1)
If you will need to change the attributes or the string content later, you should use NSMutableAttributedString
. The declarations are very similar:
// Create a blank attributed string
let mutableAttrString1 = NSMutableAttributedString()
// Initialize with a string only
let mutableAttrString2 = NSMutableAttributedString(string: "Hello.")
// Initialize with a string and inline attribute(s)
let mutableAttrString3 = NSMutableAttributedString(string: "Hello.", attributes: [NSAttributedString.Key.myName: "A value"])
// Initialize with a string and separately declared attribute(s)
let myAttributes2 = [ NSAttributedString.Key.foregroundColor: UIColor.green ]
let mutableAttrString4 = NSMutableAttributedString(string: "Hello.", attributes: myAttributes2)
As an example, let's create the attributed string at the top of this post.
First create an NSMutableAttributedString
with a new font attribute.
let myAttribute = [ NSAttributedString.Key.font: UIFont(name: "Chalkduster", size: 18.0)! ]
let myString = NSMutableAttributedString(string: "Swift", attributes: myAttribute )
If you are working along, set the attributed string to a UITextView
(or UILabel
) like this:
textView.attributedText = myString
You don't use textView.text
.
Here is the result:
Then append another attributed string that doesn't have any attributes set. (Notice that even though I used let
to declare myString
above, I can still modify it because it is an NSMutableAttributedString
. This seems rather unSwiftlike to me and I wouldn't be surprised if this changes in the future. Leave me a comment when that happens.)
let attrString = NSAttributedString(string: " Attributed Strings")
myString.append(attrString)
Next we'll just select the "Strings" word, which starts at index 17
and has a length of 7
. Notice that this is an NSRange
and not a Swift Range
. (See this answer for more about Ranges.) The addAttribute
method lets us put the attribute key name in the first spot, the attribute value in the second spot, and the range in the third spot.
var myRange = NSRange(location: 17, length: 7) // range starting at location 17 with a lenth of 7: "Strings"
myString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: myRange)
Finally, let's add a background color. For variety, let's use the addAttributes
method (note the s
). I could add multiple attributes at once with this method, but I will just add one again.
myRange = NSRange(location: 3, length: 17)
let anotherAttribute = [ NSAttributedString.Key.backgroundColor: UIColor.yellow ]
myString.addAttributes(anotherAttribute, range: myRange)
Notice that the attributes are overlapping in some places. Adding an attribute doesn't overwrite an attribute that is already there.