I have tried everything... Many articles and stackoverflow posts but I just can't seem to get it right. Scenario: I get back html code from a web service, very simple example is:
"<b>TEST</b>"
I convert this string to attributedString like this:
extension String {
var htmlToAttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else { return NSAttributedString() }
do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
} catch {
return NSAttributedString()
}
}
}
And then I display it like this:
self.textView.attributedText = htmlTextFromWebService.htmlToAttributedString
Perfect, my UITextView displays "TEST" as bold.
Now the problem: I am trying to send it back but the bold is gone.
Here's how I'm doing that:
let attrString = NSAttributedString(string: self.textView.text)
var resultHtmlText = ""
do {
let r = NSRange(location: 0, length: attrString.length)
let att = [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType]
let data = try attrString.data(from: r, documentAttributes: att)
if let h = String(data: data, encoding: .utf8) {
resultHtmlText = h
}
} catch {
print("FAILED TO CONVERT TO HTML")
}
resultHtmlText is now:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px}
span.s1 {font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt}
</style>
</head>
<body>
<p class="p1"><span class="s1">TEST</span></p>
</body>
</html>
But the web service cannot read this and plus, the bold tag is gone!
So ultimately, how can I get resultHtmlText to be this:
"<b>TEST</b>"
I just want simple html tags as my result to send to the web service!
Thanks.
Maddy's answer identifies the narrow technical issue that your code was pulling the plain String from the text property of UITextView rather than the full NSAttributedString available from the attributedText property. That answer addresses the issue of your text losing the bold styling. If you correct your code as indicated, the bold styling will be preserved, but not in the manner that you desire. More specifically, the bold styling will be included in the style sheet portion of the header of the HTML document--it will not appear as an inline <b> tag.
However, your question is broader. If I may, the essence of your question is focussed on how to make a proper round trip with the HTML code? Without knowing more about your use case, I'm not sure I can fully answer that question. For what they are worth, I offer the following comments.
DISPLAYING HTML
If you have HTML code to display, the best way to do so in iOS is using a WKWebView, not a UITextView. There are LOTS of web view examples on StackOverflow and elsewhere, such as this tutorial.
For your use case, you'll want to use the loadHTMLString(:baseURL:) to insert the HTML code into the web view. A very stripped down example using the variable name from your code, as follows:
// ... embedded somewhere in a UIViewController
let webView = WKWebView()
self.view = webView
webView.loadHTMLString(htmlTextFromWebService, baseURL: nil)
EDITING HTML
If you also need to allow the user to edit the text, then you have a MUCH BIGGER TASK ahead of you. In that case, I suggest exploring GitHub for examples of attributed text editors built on WKWebView, like RichEditorView.
SENDING HTML CODE TO YOUR WEB SERVICE
Your example uses the NSAttributedString method data(from:documentAttributes:) to create an HTML code representation of an attributed string. That method creates a fully-formed HTML document, with a full header, and uses a style sheet in the header to provide the styling.
You state the the web service is unable to handle this text. I suspect your web service takes merely raw HTML code without the header, style sheet, etc. If so, the data(from:documentAttributes:) method isn't going to work for this purpose.
One solution is to avoid the round-trip conversion issues by working with the original HTML code at all times via WKWebView. See, above.
Another solution is to write your own, simple NSAttributedString-to-HTML parser. It seems like you might be using very simple text and styling. If so, the implementation of this sort of parser should be straightforward. If you need an example of that sort of parser, leave a comment, and I will provide some sample code.
Change
let attrString = NSAttributedString(string: self.textView.text)
To
let attrString = self.textView.attributedText
This will give back the attributes but it will still give a full HTML result.
Related
Can you show a html <img src=''> inside a NSAttributedString used in an UILabel?
Yes you can, even though Apple doesn't recommended it:
The HTML importer should not be called from a background thread (that is, the options dictionary includes documentType with a value of html). It will try to synchronize with the main thread, fail, and time out. Calling it from the main thread works (but can still time out if the HTML contains references to external resources, which should be avoided at all costs). The HTML import mechanism is meant for implementing something like markdown (that is, text styles, colors, and so on), not for general HTML import.
An UILabel's attributedText can be used to render html img tags.
Here an example:
let str = "<img src='https://www.codeterritory.com/assets/screenshot_sidiusnova_04-f5422e9916fb114e73484758278c284e.jpg'>"
let data = str.data(using: String.Encoding.unicode)!
do {
let attrStr = try NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html], documentAttributes: nil)
let label = UILabel(frame: UIScreen.main.bounds)
label.attributedText = attrStr
UIApplication.shared.windows.first!.addSubview(label)
} catch let error {
print(error)
}
I have a text in HTML format. I am using the property of NSAttributed string to parse it. It pareses the text nicely and displays on the label. However, the parsing of the anchor tag doesn't make the link clickable. This is the following code that I am using for parsing.
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 ?? ""
}
When I run the app and give the value to the label as:
text = "<p>This is Google Home</p>"
simpleTextLabel.attributedText = text.htmlToAttributedString
The output on the iOS App looks like following but nothing happens on clicking :
This is Google Home.
How can I make it open in safari?
From your line:
simpleTextLabel.attributedText = text.htmlToAttributedString
We can assume that simpleTextLabel is a UILabel.
It's basic behavior from a UILabel to not be "interactable". You can add a tap gesture on it, but it transform it as a UIButton.
There are some tricks to make it possible with a UILabel, find where exactly it has been tapped, check if there is a link, etc.
Looking for "UILabel Clickable": Create tap-able "links" in the NSAttributedString of a UILabel? etc. There are even a few third party libs.
I (in my humble opinion) consider it as a "hack".
There is a good WWDC 2018 Session: TextKit Best Practices. At 2:34, it explains that if you need to interact with a shown text, prefers UITextView over UILabel.
There is a UITextViewDelegate method just for that: textView(_:shouldInteractWith:in:interaction:)
Note that there a small differences in the rendering of a UITextView and a UILabel. If you superpose them, they won't have the same "start point", the layout is a little different. However, with small changes, it can be the same (for instance: How can I make a UITextView layout text the same as a UILabel?).
Note also that according to the small modifications of a UITextView into a UILabel visual rendering, the user won't notice the difference (and that's in fact what matters, beside that using native methods of the UITextView/UITextViewDelegate make it easily understandable afterwards by another developer, or in a few months if you need to do a small modification).
I would like to perform web test and learning like some makes music by sampling. So, I would like to make a new design by compozing with them and add my touch.
Say I have the html, css, js, etc files from the site owner, I imagine it is possible to automatically build templates and layouts as HALM and LESS or SASS files from them. For example, a html parser may find nested common structures in pages. A css parser may find common constants and replace them by variables.
Does such tool already exists ? Or what could be the cavits to develop one ?
Exemple for CSS:
From:
h1 { background-color: #ff14a6; }
h2 { color: #ff14a6; }
To:
$primary: #ff14a6;
h1 { background-color: $primary; }
h2 { color: $primary; }
HTML is formed from: Layout + Template, where Layout is the overall structure of the HTML page. Here is a Ruby example:
File: layout.erb
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> ... etc.
</head>
<body>
<%= yield =>
</body>
</html>
Template replaces layout's yield.
Some layout content is page-specific, while template contains frontmatter. In template (contact.html.erb):
---
title: "Contact Information"
---
In layout (layout.erb):
<title> <%= current_page.data.title %>
</title>
If you want to create a template for whatever information you want to display your best bet is a JSON container to hold the new data and your javascript + jquery to import the data to the html. This could be done with a separate js file for the new data or the same one as the app/web page constructor js file.
This way you could have a set template layout for importing your data and then just change the file if you want to use different data. Alternatively using a framework like knockoutjs or something similar could allow you to do this as well, depending ofc on what you are familiar with or like to use.
The following snippet is a demo of jQuery using data from a JSON object to place data in the HTML (this would be the script.js file):
var view = {
displayStuff: function(obj){
var HTMLwelcomeMsg = '<span class="welcome-message">%data%</span>';
var formattedWelcome = HTMLwelcomeMsg.replace("%data%", obj.welcomeMessage);
$("#header").append(formattedWelcome);
}}
The JSON data can be in a separate file loaded by the JS or the HTML:
var model = {
"dataSample1" : "Some data",
"welcomeMessage" : "Welcome to the sample website",
"dataSample2" : "Some more data",
"someNumbers" : [1,1,253,669],
"moreVars" : {
"someMoreData" : ["123 31st st", "311 2nd st"],
"phoneNums" : ["555-5555", "999-999"]
}
}
Or you can put the data inside the JS file you use to append the elements to the HTML.
Then you call your view from the controller:
var controller = function(){
view.displayStuff(model);
}
and it all just works.
I am trying to obtain the data within the header of a web page that is being displayed in a UIWebView.
How do I get the raw (unformatted) HTML string from the UIWebView?
Also, I'm using iOS 9.
My question is similar to Reading HTML content from a UIWebView , but this post is from 6 years ago.
From the top answer on the question you linked:
NSString *html = [yourWebView stringByEvaluatingJavaScriptFromString:
#"document.body.innerHTML"];
would translate into Swift:
let html = yourWebView.stringByEvaluatingJavaScriptFromString("document.body.innerHTML")
stringByEvaluatingJavaScriptFromString returns a optional, so you'd probably want to later use an if let statement:
if let page = html {
// Do stuff with the now unwrapped "page" string
}
swift 4:
if let html = self.webView.stringByEvaluatingJavaScript(from: "document.body.innerHTML"){
}
Don't forgot get the html when the page render finished, if you got html too early, the result will be empty.
func webViewDidFinishLoad(webView: UIWebView) {
print("pageDidFinished")
if let html = webView.stringByEvaluatingJavaScriptFromString("document.documentElement.outerHTML") {
print("html=[\(html)]")
}
}
I am sending newsletter like below with Springframework 3.
private void sendMail(Map<String,Object> mailInfo) throws Exception{
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.myhost.com");
mailSender.setPort(587);
mailSender.setUsername("email#email.com");
mailSender.setPassword("12345");
MimeMessage msg = mailSender.createMimeMessage();
MimeMessageHelper mHelper = new MimeMessageHelper(msg, true, "UTF-8");
mHelper.setFrom(new InternetAddress(
mailInfo.get("send_mail").toString(), mailInfo.get("send_name").toString()));
mHelper.setTo(new InternetAddress(
mailInfo.get("recv_mail").toString(), mailInfo.get("recv_name").toString()));
mHelper.setText(mailInfo.get("mail_desc").toString(), true);
mHelper.setSubject(mailInfo.get("mail_title").toString());
mailSender.send(msg);
}
In my case value of mail_desc is an HTML(it has css and other resources). Mail goes well, but its CSS and all of images are broken.
I appended to all of src value like below in JSP
function getDomain(){
var DNS = location.href;
DNS = DNS.split('//');
DNS = 'http://' + DNS[1].substr(0,DNS[1].indexOf("/"));
return DNS;
}
So When I print this in browser console it returns localhost:8080/myApp/{image_src}.
However, When I open with gmail it looks quite different. it looks like...
<img src="https://ci5.googleusercontent.com/proxy/FVJ1IBTWmX0l0KPlNQVY_AkDsCL02O2Y_kZS7KACQlnXgfgNvNQvjBKpn9zIdPH84N_r-ulunXvzlMCVUOWsMG1WCjfYUFVX7VpjJ5OV5RdpV2ReZFjM9Yw=s0-d-e1-ft#http://localhost:8080/resources/gtl_portal/images/newsletter/ci.png" alt="ci" class="CToWUd">
Now I got questions like below :
How to implement newsletter in Normal? Where can I find some examples or references?(I think this can solve lots of problem here)
How to change value things looks like. it is quite tricky, since it is embedded in style attribute.:
<td height="50px" style="background:url('/resources/images/newsletter/top_bg.png') repeat-x 0 0;padding:15px">
Thanks a lot :D bb
You cant include your external css like you do normally , but you can prefer the way of wrapping the styles in the inline way (in <head> tag). So something like this,
<style>
.bigFont{
font-size:14px;
}
<style>
<body>
<p class='bigFont' >Hi , i am bigger </p>
</body>
so this looks separate instead adding style attribute to your tags , you can also avoid some code by resusing .
AFAIK , for adding inline images Spring framework has very good documentation. It is supported widely by mail clients, an example,
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);
so that you can simply use it as <img src='cid:identifier1234'>.
For advanced templating options you can integrate your web app with Apache velocity, a templating library