Using NSAttributedString for advanced formatting
What are attributed strings?
iOS (and OSX) has excellent support for attributed strings, they allow you to have rich formatting inside standard controls such as UILabel or UITextView. You can set various attributes over all or part of the string, and different string ranges may have differing attributes, which becomes very useful. Here are the non-deprecated attributes supported in iOS 11.1:
- accessibilityAlignment
- accessibilityAnnotationTextAttribute
- accessibilityAttachment
- accessibilityAutocorrected
- accessibilityBackgroundColor
- accessibilityCustomText
- accessibilityFont
- accessibilityForegroundColor
- accessibilityLanguage
- accessibilityLink
- accessibilityListItemIndex
- accessibilityListItemLevel
- accessibilityListItemPrefix
- accessibilityMarkedMisspelled
- accessibilityMisspelled
- accessibilityShadow
- accessibilityStrikethrough
- accessibilityStrikethroughColor
- accessibilitySuperscript
- accessibilityUnderline
- accessibilityUnderlineColor
- attachment
- backgroundColor
- baselineOffset
- cursor
- expansion
- font
- foregroundColor
- glyphInfo
- kern
- ligature
- link
- markedClauseSegment
- obliqueness
- paragraphStyle
- shadow
- spellingState
- strikethroughColor
- strikethroughStyle
- strokeColor
- strokeWidth
- superscript
- textAlternatives
- textEffect
- toolTip
- underlineColor
- underlineStyle
- verticalGlyphForm
- writingDirection
As you can see, that's alot of flexibility, and you can assign multiple attributes to differemt parts of a string, allowing the type of formatting many people would goto a webview to use, however attributed strings are much more efficient and faster than a webview.
How To Use
You create an instance of NSMutableAttributedString
or NSAttributedString
, providing possible attributes that are applicable to the whole range of the string in a dictionary. If the object is a mutable instance you can also call setAttributes
method, and provide the new attributes and the applicable range as an NSRange
object. In swift3 the key names for the dictionary fall under the NSAttributedStringKey class, with possible values as listed above. To use the string in a UIKit object, you assign the attributedText
property rather than the text
property.
let fancyText = NSMutableAttributedString(string: "Heading\nBody", attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 13.0)])
fancyText.setAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16.0)], range: NSMakeRange(0, 4))
let label = UILabel()
label.attributedText = fancyText
Example Playground
import UIKit
let demoFrame = CGRect(x: 0, y: 0, width: 320, height: 100)
let title = "Helpful Tip:"
let subtitle = "Attributed strings and views are awesome. Here is more text to ensure it wraps lines."
let normalAttributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 13.0), NSAttributedStringKey.foregroundColor : UIColor.white]
let headingAttributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16.0), NSAttributedStringKey.foregroundColor : UIColor.lightGray]
let attributedParagraph = NSMutableAttributedString(string: title + "\n" + subtitle, attributes: normalAttributes)
attributedParagraph.setAttributes(headingAttributes, range: NSMakeRange(0, title.characters.count))
let textview = UITextView(frame: demoFrame)
textview.isEditable = false
textview.showsVerticalScrollIndicator = false
textview.backgroundColor = UIColor.clear
textview.attributedText = attributedParagraph
let label = UILabel(frame: demoFrame)
label.numberOfLines = 0
label.attributedText = attributedParagraph
label
label.sizeToFit()
textview
How it looks
Conclusion
Attributed strings are sometimes overlooked, but provide a way to present text richly without using UIWebview or CoreText. Consider using them for paragraph based layout or even bullet point rendering.
Using the below constructor for NSAttributedString, you can even convert HTML markup into attributed strings.
NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)