Explain Codes LogoExplain Codes Logo

Parsing HTML into NSAttributedText - How to Set Font?

swift
attributed-text
html-parsing
font-styles
Anton ShumikhinbyAnton Shumikhin·Oct 15, 2024
TLDR

To set the font when parsing HTML into NSAttributedText, use NSAttributedString, which conveniently provides out-of-the-box HTML parsing. Here’s a snippet:

let htmlData = "<p>Your HTML here...</p>".data(using: .utf8)! let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17)] if let attributedString = try? NSAttributedString(data: htmlData, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil).mutableCopy() as? NSMutableAttributedString { attributedString.addAttributes(attributes, range: NSRange(location: 0, length: attributedString.length)) // Now use `attributedString` }

Note: Inline HTML styles will vainly attempt to upstage these settings, so show them the door: remove font-size and font-family from your HTML to allow your Swift-based font settings to steal the limelight.

Swapping Styles in Swift Versions

In the world of Swift, things evolve at a meteoric pace. The way font settings are handled across versions varies:

  • For Swift 3.0 and iOS 9+ adjust .documentType to NSDocumentTypeDocumentAttribute and .characterEncoding to NSCharacterEncodingDocumentAttribute.
  • Swift 2 is a whole different ballgame, swapping .documentType and .characterEncoding for NSUnicodeStringEncoding and NSHTMLTextDocumentType.

Dressing Up HTML with Swift

Perhaps you fancy a play at custom-hatter for your HTML string? Prepend with a <style> tag to set a default font, thus:

let htmlString = """ <style> body { font-family: '-apple-system', 'HelveticaNeue'; font-size: 17px; } </style> <p>Your HTML here...</p> """ let htmlData = htmlString.data(using: String.Encoding.utf8)! let attributedString = try? NSAttributedString(data: htmlData, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)

Post this snippet, finally achieving your ambition of becoming Swift's sartorial saint!

Customising NSMutableAttributedString

Using UIFontDescriptor

When textual traits need tweaking, UIFontDescriptor is your trusty tool. When your text needs to boldly go where no text has gone before:

let fontDescriptor = UIFont.systemFont(ofSize: 17).fontDescriptor.withSymbolicTraits(.traitBold) let boldFont = UIFont(descriptor: fontDescriptor!, size: 17) let boldAttributes: [NSAttributedString.Key: Any] = [.font: boldFont] attributedString.addAttributes(boldAttributes, range: boldRange)

Custom content just went to a new weight class!

Enumerating and updating

When rummaging through a NSAttributedString like a virtual Wardrobe of Narnia, use enumerateAttribute to uncover and update fonts. Ideal when your document is as complex as a royal coronation:

attributedString?.enumerateAttribute(.font, in: NSMakeRange(0, (attributedString?.length)!), options: []) { (value, range, stop) in if let oldFont = value as? UIFont { let newFont = oldFont.withSize(20) // Change the size for existing font. attributedString?.addAttribute(.font, value: newFont, range: range) } }

Laughing in the face of complexity, one attribute at a time!

Dress rehearsal: error-proofing

As with any dressing up exercise, mistakes happen! Employ adequate error handling to prevent wardrobe malfunctions. After all, you wouldn’t want to embarrass your HTML:

do { let attributedString = try NSAttributedString(data: htmlData, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) } catch { print("Error parsing HTML: \(error)") }

Oops, looks like the HTML forgot to put on some pants!

Encapsulating in an extension

Extend the UILabel or UITextView to handle HTML content and the font nuances, thereby re-using the code your effort deserves:

extension UILabel { func setHTML(_ html: String, fontSize: CGFloat) { guard let data = html.data(using: .utf8) else { return } do { let attributedText = try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) self.attributedText = attributedText } catch { print("Failed to set HTML: \(error)") } } }

Eureka! Efficiency just got an ally!

Dressing up Individual HTML elements

Applying inline style

Bring out the Savile Row tailoring for individual HTML tags using inline CSS within <span> tags:

<span style="font-family: 'your-font-family'; font-size: your-font-size;">Text goes here...</span>

One <span> tag, a whole new lease on life!

Dynamic styles with string interpolation

Size matters, but keeping it dynamic matters even more! Use Swift's string interpolation to set font sizes on the fly:

let fontSize = 17 let fontFamily = "HelveticaNeue" let styleString = "<style>body { font-family: '\(fontFamily)'; font-size: \(fontSize)px; }</style>"

It's like having a personal tailor at your fingertips!

Enumerating attributes efficiently

Computer says it needs a performance boost? Use NSAttributedStringEnumerationLongestEffectiveRangeNotRequired to scan large documents like a pro.