I am dinamically getting an HTML string from a Wordpress API and parsing it into an Attributed String to show it in my app. Since the string has its own styles, it shows different fonts and sizes, something that is affecting our design choices.
What I want to do is change the font and its size on the whole attributed string.
I tried doing so in the options of the attributed string, but it does nothing:
let attributedT = try! NSAttributedString(
data: nContent!.decodeHTML().data(using: String.Encoding.unicode, allowLossyConversion: true)!,
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSFontAttributeName: UIFont(name: "Helvetica", size: 16.0)!],
documentAttributes: nil)
contentLbl.attributedText = attributedT
Does anybody have any ideas on how to achieve this?
P.S. I know that I could add a CSS tag to the beginning or end of the string, but would this override other styles in it? Also, if this is a valid solution, could you please provide a sample on how to do it?
Text is a view that displays one or more lines of text in SwiftUI. By default a text view draws a string using a system font with the body text style. Text("Hello, world!") Text view with body text style. We can override this default font with a custom one. We can set a custom font for a text view using font (_:) modifier.
There are many ways to set font-weight. You can do it through both Font and Text instance methods. Set font-weight via weight (_:) modifier. This is a Font instance method, so we use this on a font, not text. Text("Hello, world!")
The font size of text represents how big that text is. To change the font size of some text, you need to use the font-size property and then specify the value in pixels ( px ), rem, or em. To get rid of the default white background and center the text both horizontally and vertically, I wrote this CSS:
Including fonts in iOS apps has gotten a lot easier in iOS8 since you can now preview them in your xibs & storyboards in Interface Builder. First we’ll add the custom font to the Xcode project so we can use it in Interface Builder. Then we’ll set up the custom font programmatically in Swift code.
Swift 4 solution
NSAttributedString
extension with convenience initializerUIFont
UIFont
, @see useDocumentFontSize
parameterguard
statementextension NSAttributedString {
convenience init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws {
let options: [NSAttributedString.DocumentReadingOptionKey : Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
let data = html.data(using: .utf8, allowLossyConversion: true)
guard (data != nil), let fontFamily = font?.familyName, let attr = try? NSMutableAttributedString(data: data!, options: options, documentAttributes: nil) else {
try self.init(data: data ?? Data(html.utf8), options: options, documentAttributes: nil)
return
}
let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize
let range = NSRange(location: 0, length: attr.length)
attr.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in
if let htmlFont = attrib as? UIFont {
let traits = htmlFont.fontDescriptor.symbolicTraits
var descrip = htmlFont.fontDescriptor.withFamily(fontFamily)
if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitBold.rawValue) != 0 {
descrip = descrip.withSymbolicTraits(.traitBold)!
}
if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitItalic.rawValue) != 0 {
descrip = descrip.withSymbolicTraits(.traitItalic)!
}
attr.addAttribute(.font, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range)
}
}
self.init(attributedString: attr)
}
}
Usage-1 (Replace font)
let attr = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!", font: UIFont.systemFont(ofSize: 34, weight: .thin))
Usage-2 (NSMutableAttributedString
example)
let attr = try! NSMutableAttributedString(htmlString: "<strong>Hello</strong> World!", font: UIFont.systemFont(ofSize: 34, weight: .thin))
attr.append(NSAttributedString(string: " MINIMIZE", attributes: [.link: "@m"]))
Usage-3 (Only convert HTML to NSAttributedString)
let attr = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
What you want to do, basically, is turn the NSAttributedString into an NSMutableAttributedString.
let attributedT = // ... attributed string
let mutableT = NSMutableAttributedString(attributedString:attributedT)
Now you can call addAttributes
to apply attributes, such as a different font, over any desired range, such as the whole thing.
Unfortunately, however, a font without a symbolic trait such as italic is a different font from a font with that symbolic trait. Therefore, you're going to need a utility that copies the existing symbolic traits from a font and applies them to another font:
func applyTraitsFromFont(_ f1: UIFont, to f2: UIFont) -> UIFont? {
let t = f1.fontDescriptor.symbolicTraits
if let fd = f2.fontDescriptor.withSymbolicTraits(t) {
return UIFont.init(descriptor: fd, size: 0)
}
return nil
}
Okay, so, armed with that utility, let's try it. I'll start with some simple HTML and convert it to an attributed string, just as you are doing:
let html = "<p>Hello <i>world</i>, hello</p>"
let data = html.data(using: .utf8)!
let att = try! NSAttributedString.init(
data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
let matt = NSMutableAttributedString(attributedString:att)
As you can see, I've converted to an NSMutableAttributedString, as I advised. Now I'll cycle thru the style runs in terms of font, altering to a different font while using my utility to apply the existing traits:
matt.enumerateAttribute(
NSFontAttributeName,
in:NSMakeRange(0,matt.length),
options:.longestEffectiveRangeNotRequired) { value, range, stop in
let f1 = value as! UIFont
let f2 = UIFont(name:"Georgia", size:20)!
if let f3 = applyTraitsFromFont(f1, to:f2) {
matt.addAttribute(
NSFontAttributeName, value:f3, range:range)
}
}
Here's the result:
Obviously you could tweak this procedure to be even more sophisticated, depending on your design needs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With