This is my version with support for text styles (body, headline, etc.), text alignment and usable directly in a UILabel
:
UILabel
extension:
import UIKit
extension UILabel {
// See: https://mcmap.net/q/982269/-can-i-set-default-font-when-translating-html-into-nsattributedstring
func set(html: String, textAligment: NSTextAlignment = NSTextAlignment.natural) {
let textStyle = self.font.fontDescriptor.object(forKey: UIFontDescriptor.AttributeName.textStyle) as! UIFont.TextStyle
do {
self.attributedText = try html.htmlAttributedString(textAligment: textAligment).with(textStyle: textStyle)
} catch let e {
print("Couldn\'t parse \'\(html)\': \(e.localizedDescription)")
}
}
}
String
extension:
import Foundation
extension String {
enum StringToHTMLError: Error {
case encoding
}
func htmlAttributedString(textAligment: NSTextAlignment = NSTextAlignment.natural) throws -> NSMutableAttributedString {
guard let data = self.data(using: String.Encoding.utf8, allowLossyConversion: false) else {
throw StringToHTMLError.encoding
}
let text = try NSMutableAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil )
// See: https://mcmap.net/q/349544/-centering-text-in-a-uilabel-with-an-nsattributedstring
let style = NSMutableParagraphStyle()
style.alignment = textAligment
text.addAttributes([.paragraphStyle: style],
range: NSMakeRange(0, text.length))
return text
}
}
And NSMutableAttributedString
extension:
import Foundation
extension NSMutableAttributedString {
func with(textStyle: UIFont.TextStyle) -> NSMutableAttributedString {
self.enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, self.length), options: .longestEffectiveRangeNotRequired, using: { (value, range, stop) in
let originalFont = value as! UIFont
if let newFont = applyTraitsFromFont(originalFont, to: textStyle) {
self.addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
}
})
return self
}
private func applyTraitsFromFont(_ f1: UIFont, to textStyle: UIFont.TextStyle) -> UIFont? {
let originalTrait = f1.fontDescriptor.symbolicTraits
let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle)
if originalTrait.contains(.traitBold) {
var traits = fontDescriptor.symbolicTraits
traits.insert(.traitBold)
if let fd = fontDescriptor.withSymbolicTraits(traits) {
return UIFont.init(descriptor: fd, size: 0)
}
}
return UIFont(descriptor: fontDescriptor, size: 0.0)
}
func with(font: UIFont) -> NSMutableAttributedString {
enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, length), options: .longestEffectiveRangeNotRequired, using: { (value, range, stop) in
if let originalFont = value as? UIFont, let newFont = applyTraitsFromFont(originalFont, to: font) {
addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
}
})
return self
}
private func applyTraitsFromFont(_ originalFont: UIFont, to newFont: UIFont) -> UIFont? {
let originalTrait = originalFont.fontDescriptor.symbolicTraits
if originalTrait.contains(.traitBold) {
var traits = newFont.fontDescriptor.symbolicTraits
traits.insert(.traitBold)
if let fontDescriptor = newFont.fontDescriptor.withSymbolicTraits(traits) {
return UIFont.init(descriptor: fontDescriptor, size: 0)
}
}
return newFont
}
}
Use:
<yourUILabel>.set(html: "Hello, <b>world</b>!",
textAligment: NSTextAlignment.center)
I hope this helps someone,
Xavi