How to display links and images from an HTML String - html

I have a string that contains HTML code. What is the best way to display that (it contains images), also I want to make links in that tappable (open in Safari)
I have tried String extension that gives me NSAttributedString from HTML, but the image is only partially shown and links are not tappable.
let text = htmlString.attributedString(withRegularFont: UIFont.systemFont(ofSize: 14), andBoldFont: UIFont.systemFont(ofSize: 16))
extension String {
func attributedString(withRegularFont regularFont: UIFont, andBoldFont boldFont: UIFont, textColor: UIColor = UIColor.gray) -> NSMutableAttributedString {
var attributedString = NSMutableAttributedString()
guard let data = self.data(using: .utf8) else { return NSMutableAttributedString() }
do {
attributedString = try NSMutableAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)
let range = NSRange(location: 0, length: attributedString.length)
attributedString.enumerateAttribute(NSAttributedString.Key.font, in: range, options: .longestEffectiveRangeNotRequired) { value, range, _ in
let currentFont: UIFont = value as! UIFont
var replacementFont: UIFont? = nil
if currentFont.fontName.contains("bold") || currentFont.fontName.contains("Semibold") {
replacementFont = boldFont
} else {
replacementFont = regularFont
}
let replacementAttribute = [NSAttributedString.Key.font:replacementFont!, NSAttributedString.Key.foregroundColor: textColor]
attributedString.addAttributes(replacementAttribute, range: range)
} catch let e {
print(e.localizedDescription)
}
return attributedString
}
}
It shows me the HTML inside the UILabel but I am not able to tap on links and images are cropped respective to device width.

I think that the best option is to save this html string as a file and then load this file using web view.
check this question

Related

NSAttributedString change color and font size

Hi guys I have small problem, I'm using func:
static func convertFromHTMLString(_ input: String?) -> NSAttributedString? {
guard let input = input else { return nil }
guard let data = input.data(using: String.Encoding.unicode, allowLossyConversion: true) else { return nil }
return try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html], documentAttributes: nil)
}
to read my Strings from Localizable.strings with attributes like this:
But when I run my app it looks that:
This change my Label color to black and font size to something like 10-12 ;/
My Label should have white color and font size 17, anyone know how to fix it?
Thanks ! :)
#Edit1
The solution must look like this
This is how it looks on Android.
func convertFromHTMLString(_ input: String?) -> NSAttributedString? {
guard let input = input else { return nil }
guard let data = input.data(using: String.Encoding.unicode, allowLossyConversion: true) else { return nil }
if let string = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html], documentAttributes: nil).string
{
//Set color and font
let myAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.white , NSAttributedString.Key.font: UIFont(name: "Chalkduster", size: 17.0)! ]
let myAttrString = NSAttributedString(string: string, attributes: myAttribute)
return myAttrString
}
return nil
}

String interpolation in attributed text from HTML (Swift 4)

I am displaying text in my app as an attributed string from local HTML files, populating a label, as this gives me formatting flexibility. Why is the usual string interpolation not working in this case, and is there a workaround? The aim is to allow the user-provided username to be contained within the string. It functions well except leaves "(user)" from the HTML file displayed in the label rather than interpolating the username as I expected. I'm still learning so if this is a strange and unworkable way to be doing things anyway then please let me know...
This is my code:
class ArticleViewController: UIViewController {
#IBOutlet weak var contentField: UITextView!
var articleID : String = ""
var user = UserDefaults.standard.object(forKey: "user") ?? "user"
override func viewDidLoad() {
super.viewDidLoad()
if let html = Bundle.main.path(forResource: "\(articleID)", ofType: "html") {
let urlToLoad = URL(fileURLWithPath: html)
let data = NSData(contentsOf: urlToLoad)
if let attributedString = try? NSAttributedString(data: data as! Data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
contentField.attributedText = attributedString
}
}
}
}
Thanks for your help!
Why is the usual string interpolation not working in this case
Usual string interpolation works on the String literals in the Swift source files, not on the content of general text files or html files.
You may need to replace the occurrences of (user) in the attributed string. (The basic concept is not different from Carpsen90's answer, but you need to be careful when replacing already attributed string.)
if let htmlURL = Bundle.main.url(forResource: articleID, withExtension: "html") {
do {
let data = try Data(contentsOf: htmlURL)
let attributedString = try NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
//### When you want to compare the result...
//originalText.attributedText = attributedString
let regex = try! NSRegularExpression(pattern: "\\(user\\)")
let range = NSRange(0..<attributedString.string.utf16.count)
let matches = regex.matches(in: attributedString.string, range: range)
for match in matches.reversed() {
attributedString.replaceCharacters(in: match.range, with: user)
}
contentField.attributedText = attributedString
} catch {
// Do error processing here...
print(error)
}
}
Example.
article.html:
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<i>(user)</i>😀<b>(user)</b>
</body>
</html>
What you can see in the text view:
You'll have to find and replace the occurrences of (user) in attributedString.
This should work:
import Foundation
import UIKit
var myString : NSAttributedString = NSAttributedString(string: "Hello (user), this is a message for you")
let regex = try! NSRegularExpression(pattern: "\\(user\\)", options: .caseInsensitive)
let range = NSMakeRange(0, myString.string.count)
let newString = regex.stringByReplacingMatches(in: myString.string, options: [], range: range, withTemplate: "CJDSW18")
let newAttribuetdString = NSAttributedString(string: newString, attributes: myString.attributes(at: 0, effectiveRange: nil))
print(newAttribuetdString.string)

iOS 11 changes font family while converting HTML to NSAttributedString

I have a string extension that converts HTML to an NSAttributedString, but i add my own custom font to the HTML string from an API before formatting in the following way.
let testString = "<span style="font-family: MuseoSans-900"> This is an html String <i> with italics </i> and a custom font </span>"
guard let htmlData = testString.data(using: .utf8) else { return nil }
do {
let attrString = try NSAttributedString(data: htmlData, options: [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue,
], documentAttributes: nil)
let formattedHTMLString = NSMutableAttributedString(attributedString: HTMLString)
formattedHTMLString.beginEditing()
formattedHTMLString.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, formattedHTMLString.length), options: []){
(_ value: Any?, _ range: NSRange, _ stop: UnsafeMutablePointer) -> Void in
if value != nil {
var oldFont = value as? UIFont
print("\(oldFont?.fontName)")
}
}
return formattedHTMLString
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
On iOS 10 < the attributed string is returned with the entire string having the same font (in this case MuseoSans-900) including the italized part of the html string but on iOS 11 it updates and changes the font to MuseoSans-300 for the italics part of the html string and keeps the rest of the string MuseoSans-900 during the conversion to NSAttributed string..I do not know why this is happening and any help would be greatly appreciated.

NSAttributedString to HTML in iOS Swift 3

I have searched a lot but can only find HTML to plain text, not the other way around, I have email implementation in my app, thus need to send the content of email as HTML to the backend.
Edit 1: I have rich text that includes bold, italic, ordered/unordered list, underlined words.
If you are looking to convert NSAttributedString to String, here is the extension method you are looking for. Simply call yourAttributtedString.htmlString() and print it out.
extension NSAttributedString {
func htmlString() -> String? {
let documentAttributes = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
do {
let htmlData = try self.data(from: NSMakeRange(0, self.length), documentAttributes:documentAttributes)
if let htmlString = String(data:htmlData, encoding:String.Encoding.utf8) {
return htmlString
}
}
catch {}
return nil
}
}
According to this post:
private func getHtmlLabel(text: String) -> UILabel {
let label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.attributedString = stringFromHtml(string: text)
return label
}
private func stringFromHtml(string: String) -> NSAttributedString? {
do {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
if let d = data {
let str = try NSAttributedString(data: d,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
return str
}
} catch { }
return nil
}

Display attributed text in textview in swift 3?

I want to display text in Italic ,bold which is received from server .
< p > < b > < i > hello < /i > < /b > < i >world< /i >< /p > so,
responseObj!["text"] = "<p><b><i>hello</i></b><i>world</></p>"
if let postText:String = responseObj!["text"] as? String{
let str = try NSAttributedString(data: postText.data(using: String.Encoding.unicode, allowLossyConversion: true)!, options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
self.textView.attributedText = str
}
when i am adding like this means my text displays normal without applying bold & italic . I want text to be displayed in bold ,italic .
EDIT: Updated Swift 4
Create an extension for String:
extension String {
func htmlAttributedString(fontSize: CGFloat = 17.0) -> NSAttributedString? {
let fontName = UIFont.systemFont(ofSize: fontSize).fontName
let string = self.appending(String(format: "<style>body{font-family: '%#'; font-size:%fpx;}</style>", fontName, fontSize))
guard let data = string.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString (
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil) else { return nil }
return html
}
}
Invoke it anywhere in your project:
text.htmlAttributedString() // will set fontSize with 17.0 which is the default value
text.htmlAttributedString(fontSize: 14.0) // pass your required fontSize
Note, that let string = self.appending(String(format: "<style>body{font-family: '%#'; font-size:%fpx;}</style>", fontName, fontSize)) is used to keep the string font as same as the font used in default iOS platform.
To set the string on UITextView use:
textView.attributedText = textToBeConverted.htmlAttributedString() ?? ""
RESULT:
Assign Attributed text to UITextView from below function,
//HTML to Attributed text
func stringFromHtml(string: String) -> NSAttributedString? {
do {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
if let d = data {
let str = try NSAttributedString(data: d,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
return str
}
} catch {
}
return nil
}
//This will give you attributed text which you have to assign to your UITextView. and whatever string having html tag that you have to pass inside this function.
//Below code will conver attributed text to HTML Text
//Attributed to HTML text
let attrStr = self.txtView.attributedText
let documentAttributes = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
do {
let htmlData = try attrStr?.data(from: NSMakeRange(0, (attrStr?.length)!), documentAttributes:documentAttributes)
if let htmlString = String(data:htmlData!, encoding:String.Encoding.utf8) {
print(htmlString)
}
}
catch {
print("error creating HTML from Attributed String")
}