How to load html text in watchkit WKInterfaceLabel in ios? - html

I have on WKInterfaceLabel in which i can not load HTML Tag like
<h1>Grishneshwar Jyotirling</h1>
and I can't use NSMutableAttributedString so please help me to finding regarding things.

var htmlText = "<h1>Grishneshwar Jyotirling</h1> Grishneshwar Jyotirling"
let attributeText: NSAttributedString?
if let htmlData = htmlText.dataUsingEncoding(NSUnicodeStringEncoding) {
do {
attributeText = try NSAttributedString(data: htmlData , options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
myLabel.setAttributedText(attributeText)
}catch let e as NSError {
print("Couldn't translate \(htmlText): \(e.localizedDescription) ")
}

Related

NSAttributedString from HTML with Hyperlinks

I am attempting to render a large HTML string, that contains hyperlinks, into a UITextView using NSAttributedString. Everything is working fine except the hyperlinks, they don't actually open the link.
For an example, here is a dummy version of my html string:
let htmlString = "<html><p>If you would like to contact someone, you can email
them at <a class=rvts10 href=\"mailto:some#one.com\">some#one.com</a></p></html>"
I have a function called convertHTML() that converts strings to NSAttributedString with html document type options, that I use to assign to the UITextView's attributed text:
textView.attributedText = htmlString.convertHTML()
The TextField is selectable but not editable. When the page is loaded, you can see the hyperlink styling (blue text) and everything, but you can't tap on the link and open the mail app.
I assume this I need to change "mailto:..." to something else that iOS will recognize, but I just have no idea what needs to be done to allow this link to be linkable.
This is my html method:
func convertHtml() -> NSAttributedString{
guard let data = data(using: .utf8) else { return NSAttributedString() }
do{
return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
}catch{
return NSAttributedString()
}
}
I think you have error with your convertHTML() Method check this
let htmlString = "<html><p>If you would like to contact someone, you can email them at <a class=rvts10 href=\"mailto:some#one.com\">some#one.com</a></p></html>"
// you have to convert string to data
let data = Data(htmlString.utf8)
// then convert data to NSAttributedString with NSAttributedString.DocumentType.htm
if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
self.textView.attributedText = attributedString
}
I use this extension :
import Foundation
extension NSAttributedString {
convenience init(htmlString html: String) throws {
try self.init(data: Data(html.utf8), options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil)
}
}
After implementing you can use it like this:
contentTextField.attributedText = try? NSAttributedString(htmlString: aHTMLString)

showing html content in TextView efficiently

I'm using this way and it works fine, but has a slowness fallback because of the usage of NSHTMLTextDocumentType as i did my research
do {
let attributedOptions:[String: Any] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue]
let date = html.data(using: String.Encoding.utf8, allowLossyConversion: true)!
return try NSAttributedString(data: data, options: attributedOptions , documentAttributes: nil)
} catch let error as NSError {
print("htmo2String \(error)")
}
any ideas how to do it faster or another efficient way to do it!
Maybe you can execute the parse code on a queue...
func parse(_ html: String, completionHandler: #escaping (_ attributedText: NSAttributedString?) -> (Void)) -> Void
{
let htmlData = text.data(using: String.Encoding.utf8, allowLossyConversion: false)
let options: [String: AnyObject] = [
NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType as AnyObject
]
completionHandler(try? NSAttributedString(data: htmlData!, options: options, documentAttributes: nil))
}
And now call the function and wait from response...
let queue: DispatchQueue = DispatchQueue(label: "com.yourcompany.Process./html_converter")
queue.async
{
parse("<p>¡Hola mundo</p>", completionHandler: { (attributtedString: NSAttributedString?) -> (Void) in
if let attributtedString = attributtedString
{
DispatchQueue.main.async
{
print("str:: \(attributtedString)")
}
}
})
}
Did you try o use a UIWebView to present HTML content ?
You can display HTML from a string or from a URL, as you prefer.
Here is an example to display HTML from a string :
string sHTMLContent = "<html><head><body><p>Hello World</p></body></head></html>";
m_WebView.LoadHtmlString(sHTMLContent , null);
Then you can set the size of your Webview to be equal to your textview with constraints. The webview will be scrollable automatically if needed.
Final string extension to show html string efficiently with #Adolfo async idea
with the ability to change font and color ^_^
extension String {
func html2StringAsync(_ fontSize: CGFloat? = nil, color: UIColor? = nil, completionBlock:#escaping (NSAttributedString) ->()) {
let fontSize = fontSize ?? 10
let fontColor = color ?? UIColor.black
let font = "Avenir !important"
let html = "<div style=\"font-family:\(font); font-size:\(fontSize)pt; color:\(fontColor.hexString);\">" + self + "</div>"
if let data = html.data(using: String.Encoding.utf8, allowLossyConversion: true){
DispatchQueue.main.async {
do {
let attributedOptions:[String: Any] = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue]
let attrStr = try NSAttributedString(data: data, options: attributedOptions , documentAttributes: nil)
completionBlock(attrStr)
} catch let error as NSError {
print("htmo2String \(error)")
}
}
}else{
completionBlock(NSAttributedString(string: self))
}
}
}

How do I edit the attributes of an NSAttributedString?

I want to be able to edit the HTML I get from my api request, changing the font and increasing among other things.
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
var dataString:String = NSString(data: data, encoding: NSUTF8StringEncoding)! as String
dispatch_async(dispatch_get_main_queue()) {
var attributedString:NSAttributedString = NSAttributedString(data: data, options:[NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding], documentAttributes: nil, error: nil)!
self.textView.attributedText = attributedString
}
}
To access the properties correctly you should be using an NSMutableAttributeString.
Change your String instantiation to this:
var attributedString: NSMutableAttributedString = NSMutableAttributedString(data: data, options:[NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding], documentAttributes: nil, error: nil)!
And add an attribute like so:
mutableString.addAttribute(NSFontAttributeName, value: UIFont(name: "Avenir", size: 18.0)!, range: NSRange(location:2,length:4))
This is just an example, you can create your own attributes to suit your needs.

How to display html formatted text in ios label

I would like to display html formatted text on a UILabel in IOS.
In Android, it has api like this .setText(Html.fromHtml(somestring));
Set TextView text from html-formatted string resource in XML
I would like to know what / if there is an equivalent in ios?
I search and find this thread:
How to show HTML text from API on the iPhone?
But it suggests using UIWebView. I need to display html formatted string in each table cell, so I think have 1 webview per row seems a bit heavy.
Is that any other alternative?
Thank you.
Swift 3.0
do {
let attrStr = try NSAttributedString(
data: "<b><i>text</i></b>".data(using: String.Encoding.unicode, allowLossyConversion: true)!,
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
label.attributedText = attrStr
} catch let error {
}
for Swift 2.0:
var attrStr = try! NSAttributedString(
data: "<b><i>text</i></b>".dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
label.attributedText = attrStr
Swift 4
import UIKit
let htmlString = "<html><body> Some <b>html</b> string </body></html>"
// works even without <html><body> </body></html> tags, BTW
let data = htmlString.data(using: String.Encoding.unicode)! // mind "!"
let attrStr = try? NSAttributedString( // do catch
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil)
// suppose we have an UILabel, but any element with NSAttributedString will do
label.attributedText = attrStr
Supplement: controlling the font of resulting formatted string
To use properly scaled (i.e. with respect to user settings) system (or any other) font you may do the following.
let newFont = UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: UIFont.systemFontSize)) // The same is possible for custom font.
let mattrStr = NSMutableAttributedString(attributedString: attrStr!)
mattrStr.beginEditing()
mattrStr.enumerateAttribute(.font, in: NSRange(location: 0, length: mattrStr.length), options: .longestEffectiveRangeNotRequired) { (value, range, _) in
if let oFont = value as? UIFont, let newFontDescriptor = oFont.fontDescriptor.withFamily(newFont.familyName).withSymbolicTraits(oFont.fontDescriptor.symbolicTraits) {
let nFont = UIFont(descriptor: newFontDescriptor, size: newFont.pointSize)
mattrStr.removeAttribute(.font, range: range)
mattrStr.addAttribute(.font, value: nFont, range: range)
}
}
mattrStr.endEditing()
label.attributedText = mattrStr
You could try an attributed string:
var attrStr = NSAttributedString(
data: "<b><i>text</i></b>".dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true),
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil,
error: nil)
label.attributedText = attrStr
Objective-C Version:
NSError *error = nil;
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:contentData
options:#{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType}
documentAttributes:nil error:&error];
This is just the Objective-C conversion of the above answers. All the answers above are right and reference taken from the above answers for this.
For me, Paul's answer worked. But for custom fonts I had to put following hack.
//Please take care of force unwrapping
let data = htmlString.data(using: String.Encoding.unicode)!
let mattrStr = try! NSMutableAttributedString(
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil)
let normalFont = UIFontMetrics.default.scaledFont(for: UIFont(name: "NormalFontName", size: 15.0)!)//
let boldFont = UIFontMetrics.default.scaledFont(for: UIFont(name: "BoldFontName", size: 15.0)!)
mattrStr.beginEditing()
mattrStr.enumerateAttribute(.font, in: NSRange(location: 0, length: mattrStr.length), options: .longestEffectiveRangeNotRequired) { (value, range, _) in
if let oFont = value as? UIFont{
mattrStr.removeAttribute(.font, range: range)
if oFont.fontName.contains("Bold"){
mattrStr.addAttribute(.font, value: boldFont, range: range)
}
else{
mattrStr.addAttribute(.font, value: normalFont, range: range)
}
}
}
Try this:
let label : UILable! = String.stringFromHTML("html String")
func stringFromHTML( string: String?) -> String
{
do{
let str = try NSAttributedString(data:string!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true
)!, options:[NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSNumber(unsignedLong: NSUTF8StringEncoding)], documentAttributes: nil)
return str.string
} catch
{
print("html error\n",error)
}
return ""
}
Hope its helpful.

Parsing HTML into NSAttributedText - how to set font?

I am trying to get a snippet of text that is formatted in html to display nicely on an iPhone in a UITableViewCell.
So far I have this:
NSError* error;
NSString* source = #"<strong>Nice</strong> try, Phil";
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
documentAttributes:nil error:&error];
This kind of works. I get some text that has 'Nice' in bold! But... it also sets the font to be Times Roman! This is not the font face I want.
I am thinking I need to set something in the documentAttributes, but, I can't find any examples anywhere.
Swift 2 version, based on the answer given by Javier Querol
extension UILabel {
func setHTMLFromString(text: String) {
let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%#</span>", text) as String
let attrStr = try! NSAttributedString(
data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
documentAttributes: nil)
self.attributedText = attrStr
}
}
Swift 3.0 and iOS 9+
extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%#</span>", htmlText)
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue],
documentAttributes: nil)
self.attributedText = attrStr
}
}
Swift 5 and iOS 11+
extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%#</span>", htmlText)
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)
self.attributedText = attrStr
}
}
#import "UILabel+HTML.h"
#implementation UILabel (HTML)
- (void)jaq_setHTMLFromString:(NSString *)string {
string = [string stringByAppendingString:[NSString stringWithFormat:#"<style>body{font-family: '%#'; font-size:%fpx;}</style>",
self.font.fontName,
self.font.pointSize]];
self.attributedText = [[NSAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)}
documentAttributes:nil
error:nil];
}
#end
This way you don't need to specify which font you want, it will take the label font and size.
I actually found a working solution to this problem:
Changing the font in your HTML response string before it gets parsed.
NSString *aux = [NSString stringWithFormat:#"<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">%#</span>", htmlResponse];
Example:
NSString *aux = [NSString stringWithFormat:#"<span style=\"font-family: HelveticaNeue-Thin; font-size: 17\">%#</span>", [response objectForKey:#"content"]];
Swift version:
let aux = "<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">\(htmlResponse)</span>"
A more generic approach is to look at the font traits while enumerating, and create a font with the same traits (bold, italic, etc.):
extension NSMutableAttributedString {
/// Replaces the base font (typically Times) with the given font, while preserving traits like bold and italic
func setBaseFont(baseFont: UIFont, preserveFontSizes: Bool = false) {
let baseDescriptor = baseFont.fontDescriptor
let wholeRange = NSRange(location: 0, length: length)
beginEditing()
enumerateAttribute(.font, in: wholeRange, options: []) { object, range, _ in
guard let font = object as? UIFont else { return }
// Instantiate a font with our base font's family, but with the current range's traits
let traits = font.fontDescriptor.symbolicTraits
guard let descriptor = baseDescriptor.withSymbolicTraits(traits) else { return }
let newSize = preserveFontSizes ? descriptor.pointSize : baseDescriptor.pointSize
let newFont = UIFont(descriptor: descriptor, size: newSize)
self.removeAttribute(.font, range: range)
self.addAttribute(.font, value: newFont, range: range)
}
endEditing()
}
}
Figured it out. Bit of a bear, and maybe not the best answer.
This code will go through all the font changes. I know that it is using "Times New Roman" and "Times New Roman BoldMT" for the fonts.
But regardless, this will find the bold fonts and let me reset them. I can also reset the size while I'm at it.
I honestly hope/think there is a way to set this up at parse time, but I can't find it if there is.
- (void)changeFont:(NSMutableAttributedString*)string
{
NSRange range = (NSRange){0,[string length]};
[string enumerateAttribute:NSFontAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont* currentFont = value;
UIFont *replacementFont = nil;
if ([currentFont.fontName rangeOfString:#"bold" options:NSCaseInsensitiveSearch].location != NSNotFound) {
replacementFont = [UIFont fontWithName:#"HelveticaNeue-CondensedBold" size:25.0f];
} else {
replacementFont = [UIFont fontWithName:#"HelveticaNeue-Thin" size:25.0f];
}
[string addAttribute:NSFontAttributeName value:replacementFont range:range];
}];
}
Swift 4+ update of UILabel extension
extension UILabel {
func setHTMLFromString(text: String) {
let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%#</span>" as NSString, text)
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: String.Encoding.unicode.rawValue, allowLossyConversion: true)!,
options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
self.attributedText = attrStr
}
}
iOS 9+
extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = NSString(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%#</span>" as NSString, htmlText) as String
//process collection values
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
self.attributedText = attrStr
}
}
Yes, there is an easier solution. Set the font in the html source!
NSError* error;
NSString* source = #"<strong>Nice</strong> try, Phil";
source = [source stringByAppendingString:#"<style>strong{font-family: 'Avenir-Roman';font-size: 14px;}</style>"];
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
documentAttributes:nil error:&error];
Hope this helps.
The answers above all work OK if you're doing the conversion at the same time as creating the NSAttributedString. But I think a better solution, which works on the string itself and therefore doesn't need access to the input, is the following category:
extension NSMutableAttributedString
{
func convertFontTo(font: UIFont)
{
var range = NSMakeRange(0, 0)
while (NSMaxRange(range) < length)
{
let attributes = attributesAtIndex(NSMaxRange(range), effectiveRange: &range)
if let oldFont = attributes[NSFontAttributeName]
{
let newFont = UIFont(descriptor: font.fontDescriptor().fontDescriptorWithSymbolicTraits(oldFont.fontDescriptor().symbolicTraits), size: font.pointSize)
addAttribute(NSFontAttributeName, value: newFont, range: range)
}
}
}
}
Use as:
let desc = NSMutableAttributedString(attributedString: *someNSAttributedString*)
desc.convertFontTo(UIFont.systemFontOfSize(16))
Works on iOS 7+
Improving on Victor's solution, including color:
extension UILabel {
func setHTMLFromString(text: String) {
let modifiedFont = NSString(format:"<span style=\"color:\(self.textColor.toHexString());font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%#</span>", text) as String
let attrStr = try! NSAttributedString(
data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
documentAttributes: nil)
self.attributedText = attrStr
}
}
For this to work you will also need YLColor.swift of the uicolor to hex conversion https://gist.github.com/yannickl/16f0ed38f0698d9a8ae7
Using of NSHTMLTextDocumentType is slow and hard to control styles. I suggest you to try my library which is called Atributika. It has its own very fast parser. Also you can have any tag names and define any style for them.
Example:
let str = "<strong>Nice</strong> try, Phil".style(tags:
Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString
label.attributedText = str
You can find it here https://github.com/psharanda/Atributika
Joining together everyone's answers, I made two extensions that allow setting a label with html text. Some answers above did not correctly interpret the font family in the attributed strings. Others were incomplete for my needs or failed in other ways. Let me know if there's anything you'd like me to improve on.
I hope this helps someone.
extension UILabel {
/// Sets the label using the supplied html, using the label's font and font size as a basis.
/// For predictable results, using only simple html without style sheets.
/// See https://stackoverflow.com/questions/19921972/parsing-html-into-nsattributedtext-how-to-set-font
///
/// - Returns: Whether the text could be converted.
#discardableResult func setAttributedText(fromHtml html: String) -> Bool {
guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
print(">>> Could not create UTF8 formatted data from \(html)")
return false
}
do {
let mutableText = try NSMutableAttributedString(
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
mutableText.replaceFonts(with: font)
self.attributedText = mutableText
return true
} catch (let error) {
print(">>> Could not create attributed text from \(html)\nError: \(error)")
return false
}
}
}
extension NSMutableAttributedString {
/// Replace any font with the specified font (including its pointSize) while still keeping
/// all other attributes like bold, italics, spacing, etc.
/// See https://stackoverflow.com/questions/19921972/parsing-html-into-nsattributedtext-how-to-set-font
func replaceFonts(with font: UIFont) {
let baseFontDescriptor = font.fontDescriptor
var changes = [NSRange: UIFont]()
enumerateAttribute(.font, in: NSMakeRange(0, length), options: []) { foundFont, range, _ in
if let htmlTraits = (foundFont as? UIFont)?.fontDescriptor.symbolicTraits,
let adjustedDescriptor = baseFontDescriptor.withSymbolicTraits(htmlTraits) {
let newFont = UIFont(descriptor: adjustedDescriptor, size: font.pointSize)
changes[range] = newFont
}
}
changes.forEach { range, newFont in
removeAttribute(.font, range: range)
addAttribute(.font, value: newFont, range: range)
}
}
}
Thanks for the answers, I really liked the extension but I have not converted to swift yet. For those old schoolers still in Objective-C this should help a little :D
-(void) setBaseFont:(UIFont*)font preserveSize:(BOOL) bPreserve {
UIFontDescriptor *baseDescriptor = font.fontDescriptor;
[self enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
UIFont *font = (UIFont*)value;
UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
UIFontDescriptor *descriptor = [baseDescriptor fontDescriptorWithSymbolicTraits:traits];
UIFont *newFont = [UIFont fontWithDescriptor:descriptor size:bPreserve?baseDescriptor.pointSize:descriptor.pointSize];
[self removeAttribute:NSFontAttributeName range:range];
[self addAttribute:NSFontAttributeName value:newFont range:range];
}]; }
Happy Coding!
--Greg Frame
Here is an extension for NSString that returns an NSAttributedString using Objective-C.
It correctly handles a string with HTML tags and sets the desired Font and Font color while preserving HTML tags including BOLD, ITALICS...
Best of all it does not rely on any HTML markers to set the font attributes.
#implementation NSString (AUIViewFactory)
- (NSAttributedString*)attributedStringFromHtmlUsingFont:(UIFont*)font fontColor:(UIColor*)fontColor
{
NSMutableAttributedString* mutableAttributedString = [[[NSAttributedString alloc] initWithData:[self dataUsingEncoding:NSUTF8StringEncoding] options:#{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute : #(NSUTF8StringEncoding)} documentAttributes:nil error:nil] mutableCopy]; // parse text with html tags into a mutable attributed string
[mutableAttributedString beginEditing];
// html tags cause font ranges to be created, for example "This text is <b>bold</b> now." creates three font ranges: "This text is " , "bold" , " now."
[mutableAttributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, mutableAttributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL* stop)
{ // iterate every font range, change every font to new font but preserve symbolic traits such as bold and italic (underline and strikethorugh are preserved automatically), set font color
if (value)
{
UIFont* oldFont = (UIFont*)value;
UIFontDescriptor* fontDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
UIFont* newFont = [UIFont fontWithDescriptor:fontDescriptor size:font.pointSize];
[mutableAttributedString removeAttribute:NSFontAttributeName range:range]; // remove the old font attribute from this range
[mutableAttributedString addAttribute:NSFontAttributeName value:newFont range:range]; // add the new font attribute to this range
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:fontColor range:range]; // set the font color for this range
}
}];
[mutableAttributedString endEditing];
return mutableAttributedString;
}
#end
Swift 5 Solution for UILabel and UITextView
extension UITextView {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%#</span>", htmlText)
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)
self.attributedText = attrStr
}
}
extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%#</span>", htmlText)
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)
self.attributedText = attrStr
}
}
Usage for UILabel
self.label.setHTMLFromString(htmlText: htmlString)
Usage for UITextView
self.textView.setHTMLFromString(htmlText: htmlString)
Output
Swift 3 String extension including a nil font. The property without font is taken from other SO question, do not remember which one :(
extension String {
var html2AttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else {
return nil
}
do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
}
catch {
print(error.localizedDescription)
return nil
}
}
public func getHtml2AttributedString(font: UIFont?) -> NSAttributedString? {
guard let font = font else {
return html2AttributedString
}
let modifiedString = "<style>body{font-family: '\(font.fontName)'; font-size:\(font.pointSize)px;}</style>\(self)";
guard let data = modifiedString.data(using: .utf8) else {
return nil
}
do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
}
catch {
print(error)
return nil
}
}
}
Swift Solution
The below approach works. You can very well provide the font family, font size, and color in this approach. Feel free to suggest changes or any better way of doing this.
extension UILabel {
func setHTMLFromString(htmlText: String,fontFamily:String,fontColor:String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', \(fontFamily); font-size: \(self.font!.pointSize); color: \(fontColor) ; \">%#</span>", htmlText)
do{
if let valData = modifiedFont.data(using: .utf8){
let attrStr = try NSAttributedString(data: valData, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html.rawValue], documentAttributes: nil)
self.attributedText = attrStr
}
}catch{
print("Conversion failed with \(error)")
self.attributedText = nil
}
}
Actually, an even easier and cleanr way exists. Just set the font after parsing the HTML:
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:#{
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
[text addAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Lato-Regular" size:20]} range:NSMakeRange(0, text.length)];