How to display HTML content in UITextview in iOS? - html

From my response, I am getting the HTML tags with some strings. I am displaying the dynamic contents in my table view. I've converted HTML tags and displaying in the table view. But the problem is, facing the table view flickering issue heavily and it affects the performance also.
Can you please suggest me, if there is any other way to improve the performance.
I am thinking to add the HTML tags manually and replace the tags based on the response. Bcoz I don't want to affect my performance. please advice.
Eg: How to get the Customer Support ?.
The below code is for converting the html code,
var encryptData : String = inputString
if inputString.contains("<") {
encryptData = inputString.htmlToString // Converting HTML to text
}
else { return encryptData } // Normal string
extension String {
var htmlToAttributedString: 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()
}
}
var htmlToString: String {
return htmlToAttributedString?.string ?? ""
}
}

Try this Approach
Cell with NSAttributedString makes the scrolling of UITableView slow
It will help you out for table view flickering. Thanks

Related

How to display HTML document correctly in UITextView

I have an UITextview inside a iOS app, where I want to display a HTML document. I found solutions to transform the HTML document to an NSAttributedString.
extension String {
var htmlToAttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else { return nil }
do {
return try NSAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
} catch {
return nil
}
}
var htmlToString: String {
return htmlToAttributedString?.string ?? ""
}
}
Now I am struggling with the fact, that the HTML document is not displayed as it should be.
For example I want to display this HTML-example inside my UITextView: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_example_website
It should look like this:
HTML document in browser
But it looks like this:
HTML document on iPad emulator inside UITextView
What am I doing wrong?
I think it better to display it in WKWebView WKWebKit Offical Documentation

Unable to show new paragraph for html data into Textview in iOS Swift

I am working on iOS (Swift) application. I am getting some server response like below.
"description":"This is sample text to show in UI. When doing everyday activities.\u003cbr /\u003eclass is a strong predictor of life, and again sample text here.\u003cbr /\u003eSample text can show here also."
So, Above text has 3 paragraphs, I am trying to displaying them in Textview, But it is showing in plain with new line instead of New Paragraph.
override func viewDidLoad() {
super.viewDidLoad()
let description = jsonResponse["description"] as! String
self.textView.attributedText = description.htmlAttributedString()
}
extension String {
func htmlAttributedString() -> NSAttributedString? {
guard let data = self.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
}
}
The issue is it is showing text, But like new line showing instead of new paragraph. How to fix this?
I have fixed this issue by following, And after conversion server response into json serialization, the special characters code showing as
So, I have fixed like below
let description = jsonResponse["description"] as! String
let formattedString = description.replacingOccurrences(of: "<br />", with: " \n\n")
self.textView.text = formattedString

Displaying HTML text in UITextView crash occurred in swift 4?

I am using this code snipped
var htmlToAttributedString: 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) // Get crash on this line
} catch let error {
print(error.localizedDescription)
return NSAttributedString()
}
}
var htmlToString: String {
return htmlToAttributedString?.string ?? ""
}
showing HTML text in UITableViewCell
cell.textViewMessage.attributedText = msg.htmlToAttributedString
Launching first time there is no crash but after that when I run the code got a crash and not working after that.
Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)
#Edit HTML String to display in cell
<p>Here\'s a short video tour. Press play to start.</p><br><iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://www.youtube.com\"></iframe><br>
#Edit 1 - I am trying to run this code in Playground and it's just working fine except now it's showing an error. Please see the attached image
Looks like the problem is the tag, not every tag can show up in uitextview.
You can display better these tag in uiwebview
I thinks the problem is iframe tag.
To display iFrame use uiwebview instead, or wkwebview.
Thanks
The reason for this issue is the table view, this error will occur very randomly and hard to reproduce because its more specific to device memory and UI draw process which might result in executing the method in the background thread. While reallocating and deallocating the table cells, deep down somewhere the table cells might call this method on a background thread while the HTML importer uses a non-thread-safe WebKit importer and expects to be on the main thread.
How to reproduce this error: Run the code using UITest and it will crash more often since the unit test slows down the UI draw process significantly
Solution: decode HTML to String should be on the main thread but do this in the model layer on main thread instead of doing it during cell creation. This will make the UI more fluid as well.
Why the crash was not caught in catch block: Your app has crashed due to an unhandled language exception, as used by the exception handling infrastructure for Objective-C. SWIFT is like a nice wrapper around Cocoa’s NSError. Swift is not able to handle Objective-C exceptions, and thus an exception through by Objective-C code in the frameworks will not be caused by your Swift error handler.
Here is a solution inspired by this repo. Basically we remove the iframe tag and replace it with clickable img:
let msg = "<p>Here\'s a short video tour. Press play to start.</p><br><iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://www.youtube.com/embed/wJcOvdkI7mU\"></iframe><br>"
//Let's get the video id
let range = NSRange(location: 0, length: msg.utf16.count)
let regex = try! NSRegularExpression(pattern: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)")
guard let match = regex.firstMatch(in: msg, options: [], range: range) else {
fatalError("Couldn't find the video ID")
}
let videoId: String = String(msg[Range(match.range, in: msg)!])
//Let's replace the the opening iframe tag
let regex2 = try! NSRegularExpression(pattern:
"<[\\s]*iframe[\\s]+.*src=")
let str2 = regex2.stringByReplacingMatches(in: msg, options: [], range: range, withTemplate: "<a href=")
//And then replace the closing tag
let regex3 = try! NSRegularExpression(pattern:
"><\\/iframe>")
let range2 = NSRange(location: 0, length: str2.utf16.count)
let str3 = regex3.stringByReplacingMatches(in: str2, options: [], range: range2, withTemplate: "><img src=\"https://img.youtube.com/vi/" + videoId + "/0.jpg\" alt=\"\" width=\"\(textView.frame.width)\" /></a>") // You could adjust the width and height to your liking
//Set the text of the textView
textView.attributedText = str3.htmlToAttributedString
textView.delegate = self
To open the Youtube app when the user taps and holds on the image, implement this delegate method:
extension NameOfYourViewController: UITextViewDelegate {
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
UIApplication.shared.open(URL, options: [:])
return true
}
}
If the youtube app is not installed, then the video will be played in Safari.
Here is the result:
Write in main async is helped me.
DispatchQueue.main.async {
self.myLabel.attributedText = self.myAtributedText
}
Try this for UITextView:
let string = "<h2>The bedding was hardly able to cover it.</h2>"
if !string.isEmpty{
if let htmlData = string.data(using:String.Encoding.unicode) {
do {
let attributedText = try NSAttributedString(data: htmlData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
cell.textViewMessage.attributedText = attributedText
} catch let e as NSError {
print("Couldn't translate \(string): \(e.localizedDescription) ")
}
}
Try this for UILabel for setting html text to uilabel with html tags:
extension String {
var withoutHtmlTags: String {
let a = self.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)
return a.replacingOccurrences(of: "&[^;]+;", with: "", options: String.CompareOptions.regularExpression, range: nil)
}
}
let string = "<h2>The bedding was hardly able to cover it.</h2>"
textUIlabel.text = string.withoutHtmlTags
Changes your options to:
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]

Does NSHTMLTextDocumentType support HTML table?

In my app, i want to display a text in a UILabel. I use HTML to store the text in my data base to dynamically from my text in my app. I actually use this (Swift 3.2, iOS 8+) :
if let data = text.data(using: .utf8) {
let htmlString = try? NSMutableAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
self.textLabel.attributedText = htmlString
}
It's work great for the HTML stuff i used like
<b>Text</b>
<i>Test</i>
And more...
Now, i want to display a table in my label. This is the HTML code for the table :
<table border="2px solid black">
<tr><th>Symbole</th><th>Å</th><th>↓</th><th>■</th><th>╩</th><th>¬</th><th>▓</th><th>Ø</th><th>±</th><th> º </th><th>¶</th><th>░</th></tr>
<tr><td>Utilisation</td><td>1</td><td>11</td><td>11</td><td>5</td><td>1</td><td>4</td><td>12</td><td>4</td><td>1</td><td>5</td><td>1</td></tr>
</table>
This code displays a table form but there is no border in the table. I want to display the table border like the reel HTML render. It's possible or not ?
Weird issue, I didn't understand why this simple thing didn't work, however I managed to make the border appear by adding a random attribute to the NSAttributedString, which makes me believe it's a NSAttributedString rendering bug.
Here's the function that I used (this is Swift 4 but can be converted to earlier versions):
extension String {
func attributedString() -> NSAttributedString? {
guard let data = self.data(using: String.Encoding.utf8,
allowLossyConversion: false) else { return nil }
let options: [NSAttributedString.DocumentReadingOptionKey : Any] = [
NSAttributedString.DocumentReadingOptionKey.characterEncoding : String.Encoding.utf8.rawValue,
NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html
]
let htmlString = try? NSMutableAttributedString(data: data, options: options, documentAttributes: nil)
// Removing this line makes the bug reappear
htmlString?.addAttribute(NSAttributedStringKey.backgroundColor, value: UIColor.clear, range: NSMakeRange(0, 1))
return htmlString
}
}

NSRegularExpression Check Content of HTML String

I get html string from server. If it's empty, we don't do anything. Otherwise, we show them on UIWebView. I can easily check with .isEmpty in a simple if statement.
Services.getBusinessProfile(countryCode: countryCode, companyId: companyData.cId) { (req, html) in
if !html.isEmpty {
// rest of the code
Problem is, sometimes I get empty tag:
<span style=\"font-family:HelveticaNeue; font-size: 16\"></span>
How can I check the content of this tag? I think I have to use NSRegularExpression for this, as this thread: NSRegularExpression to extract text between two XML tags. But I have no clue how to use it.
If you just need to retrieve the substring between the first span tag in your html text you can do it using range of string upperBound and lowerBound to get your substring as follow:
let htmlString = "<span style=\"font-family:HelveticaNeue; font-size: 16\">Indonesia</span>"
if let lower = htmlString.range(of: "<span style=\"font-family:HelveticaNeue; font-size: 16\">")?.upperBound,
let upper = htmlString.range(of: "</span>", range: lower..<htmlString.endIndex)?.lowerBound {
let text = htmlString[lower..<upper] // "Indonesia"
}
In Swift you can validate if the String that contains the HTML text will result in an empty String once parsed:
func isEmptyOrBlank(htmlStr: String) -> Bool {
let htmlData = htmlStr.data(using: String.Encoding.unicode)
do {
let attributedText = try NSAttributedString(
data: htmlData!,
options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil
)
// Remove any whitespace and new line characters
let result = attributedText.string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
return result.isEmpty
} catch let e as NSError {
// Unable to parse, so assume it's empty
return true
}
}