UIWebView loadHTMLString not working in iOS5 - iphone

Perhaps this is similar to this question, which has no responses: loadHTMLString Not Working With iOS5?
I have a UIWebView which I populate using loadHTMLString:baseURL:. The HTML is small and simple, and it references a css style sheet and a javascript script, which are loaded via the baseURL:, which is set to a directory inside the app's bundle.
// load the html
NSString* filePath = [NSString stringWithFormat: #"%#/html", [[NSBundle mainBundle] resourcePath ] ];
[_pCurrentWebView loadHTMLString: html baseURL: [NSURL fileURLWithPath: filePath isDirectory: YES ] ];
This has always worked in the past, but it is broke in iOS5. In iOS5, nothing is displayed in the UIWebView. The webview does source all of the expected events - e.g. shouldLoadRequest, didStartLoad, didFinishLoad, etc.
The html has a script tag, like this:
<script type="text/javascript" src="./myscript.js" />
If I remove the script tag then the page loads and renders fine in iOS5. And I can tell that the css file, which is referenced the same way as the script .js file, is loaded and applied.
If I keep the script tag but make the myscript.js file completely empty it still fails to load.
To me, this seems like some sort of cross-site-scripting issue - in that the WebView thinks that it should disallow loading the script (and in fact, disallow rendering of the page??)
Not sure where to go from here. Ideas?
UPDATE
This is feeling more and more like a cross-site-scripting issue. If I remove the tag it works, albeit sans script. All my images are loaded from the baseURL, as is my stylesheet. That is, we know the baseURL is working.
If I replace the tag with the actual contents of my script file then it works, so the problem is not the script itself.
Still looking for confirmation and additional ideas to circumvent. It's inconvenient for me to have to patch in the script itself into the html, but this is my best solution thus far. Alternatively I could write the html to the filesystem and load via loadRequest, but again, not my first choice.
UPDATE 2
Thanks to #djromero I have a solution. My document is a XHTML document and as such used a self-closing script tag (no content, just attributes.) But loadHTMLString:baseURL: apparently assumes a MIMEType of text/html, which the UIWebView apparently now interprets more strictly - and in text/html documents you may not have self closing tags.
My solution is to switch to loadData:MIMEtype:baseURL: and specify application/xhtml+xml as the mime type. I can easily construct the NSData from my NSString using dataUsingEncoding:.

I'm not an HTML standards expert, but...did you try to close the <script> tag:
<script type="text/javascript" src="./myscript.js"></script>
It worked for me.

The way to load an HTML file that contains embedded folder references, such as '/file.js', is to load it as a URL rather than as a STRING.
NSString *urlAddress = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"htm"];
NSURL *url = [NSURL fileURLWithPath:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];
I use this along with referenced folders (not referenced files) to create an ordinary website structure in Xcode, with js/ and css/ and images/ references in the embedded index.htm file, e.g.
<script type="text/javascript" src="js/jquery-1.6.4.min.js"></script>
UPDATE
I don't think that referencing a URI within a loaded HTML string was officially supported. If you must use a string, then load the needed resource files into the string before you load it into the UIWebView.

Related

Load UIWebView with a CSS local file

I'm looking for a way to load my UIWebView with a local CSS file that should affect on the UIWebView's loadRequest.
For the clearness:
I have an UIWebView that I loadRequest it with a website url.
I also have a local CSS file that should affect this loadRequest url.
I want to load this CSS file onto the UIWebView.
Thanks,
This stackoverflow question appears to have one or two answers that may help you.
You need to load the local CSS (using a method not unlike #Shrey uses, but looking for your CSS file), and somehow inject it into the page, and the only way appears to be to use:
[webview stringByEvaluatingJavaScriptFromString:someJavascriptToInjectCSS];
and some clever Javascript to modify the page to add the CSS in.
Hope this helps point you in the right direction. I have used this method to inject stuff into pages, so it does work, but I don't know Javascript well enough to write the code to inject your CSS into the page.
This is the Swift version.
Load the css file on the webViewDidFinishLoad delegate method and use stringByEvaluatingJavaScriptFromString.
Credits here
func loadWebViewStyles() {
guard let cssPath = NSBundle.mainBundle().pathForResource("theme", ofType: "css") else {
return
}
let loadStyles = "var script = document.createElement('link'); script.type = 'text/css'; script.rel = 'stylesheet'; script.href = '\(cssPath)'; document.getElementsByTagName('body')[0].appendChild(script);"
webView.stringByEvaluatingJavaScriptFromString(loadStyles)
}
try this:
[WebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]isDirectory:NO]]];

How to change the character encoding in UIWebView?

Summary of the problem: When browsing non-English sites that does not explicitly specify the correct character encoding with UIWebView on the iOS, the page cannot be displayed correctly.
Detailed explanation: As the loadRequest: method in UIWebView will use the encoding specified in the charset header sent from the web server or the charset written in the HTML meta tag, and default to iso-8859-1 (well, I am not too sure about this) when charset is not specified, which cause non-English sites cannot display properly.
I have tried to Google for a way to change the charset that the UIWebView use, but the only way is to use the loadData:MIMEType:textEncodingName:baseURL: method to specify the encoding name.
However, it is not a good idea to use loadData:MIMEType:textEncodingName:baseURL: + NSURLConnection instead of loadRequest:, because UIWebView won't call the delegate method webView:shouldStartLoadWithRequest:navigationType: for frames, and even if we found a way to get notified when UIWebView load a frame, we cannot call loadData:MIMEType:textEncodingName:baseURL: to load the frame content, because it will load the frame as the outer page if we do that.
Besides, I have tried to use a javascript hack, but seems that property is read-only and cannot be changed.
[webView stringByEvaluatingJavaScriptFromString:#"document.characterSet='utf-8';"];
Another workaround is inserting a meta tag to the HTML, and ask UIWebView to load the modified code, but the frame problem mentioned above also apply here.
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Question: Is there any better solution that can change the character encoding in a loaded webpage in UIWebView, and handle frames properly?
You can do this by manually loading the HTML, modifying it, and then loading the modified content into the UIWebView.
manually load the HTML from the page that doesn't include the meta tag, into a string (e.g. use NSURLConnection)
using string manipulation, insert your appropriate encoding meta tag into the manually loaded HTML
set the HTML in your web view to the modified string using loadHTMLString:
Now, this will work fine for a web page that contains no links. If it contains links, then you will find that after they click on a link, the new page will not have your modification in place. Therefore, you will need to manually intercept all of the clicks. Here's how you can do that:
Implement a UIWebView delegate
Implement the method webView:shouldStartLoadWithRequest:navigationType:
In your delegate method, load the URL manually and modify the content before setting it into the UIWebView, as above.
This is what I do,
first find out if it is text, if it is, I get the data, set the encoding and load it using UIWebView>loadData:MIMEType:textEncodingName:baseURL.
Here is the code:
Get the MIMEType sending a synchronous HEAD request.
We want to use a HEAD HTTP request which generally is fast enough.
And we want to make the request synchronously for two reasons, we need the request's result to continue, and we want to avoid concurrent request problems like this.
NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:url];
[headRequest setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *headResponse;
NSError *error = nil;
[NSURLConnection sendSynchronousRequest:headRequest
returningResponse:&headResponse
error:&error];
if (error != nil) {
NSLog(#"loadURLWithString %#",[error localizedDescription]);
}
NSString *mimeType = [headResponse MIMEType];
 
 
Then if it is text, load it with UIWebView>loadData:MIMEType:textEncodingName:baseURL
To maintain the application responsive, it is recommended to put the following code into a block and run it inside a GCD queue.
if([mimeType rangeOfString:#"text"
options:NSCaseInsensitiveSearch].location == 0) {
[wview loadData:[NSData dataWithContentsOfURL: url] MIMEType:mimeType
textEncodingName:encoding baseURL:nil];

iPhone UiWebView images with absolute path

I display some html in my UIWebView and I have in my html references to images like: '/images/test1.gif' etc.
I display this in that way:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[myWebView loadHTMLString:someHtml baseURL:baseURL];
I've also added all required images to my Xcode project, into 'images' directory. The problem is, that, as far as I can see, the baseUrl ends with '/', and path to the images starts with '/' (it's absolute). All that makes my images doesn't appear.
I've noticed, that if I change, in html, path to images, to for example 'images/test.gif', the image will show up, but I want (if possible) to avoid changing all image paths in html from absolute to relative, becasue this html is imported from some database, and in case of another import I would have to change it again.
Have you tried clipping the slash from the end of baseURL? I should hope the web view is smart enough to add a slash where needed, despite blindly concatenating two strings that result in a double slash.

UIWebView display locally stored website (HTML, images, Javascript)

I've looked EVERYWHERE for this, can't find anything. I basically need to store an entire website inside of my iPhone for some interesting reasons. Regardless I can't figure out how to display it correctly in my UIWebView.
EDIT: I should clarify, I can load the original HTML file, and I have chagned all of the pathing to be local, except nothing gets linked in.
Here is the code
self.dataDetectorTypes = UIDataDetectorTypeLink;
NSString *path = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSURL *url = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self loadRequest:request];
index.html has a bunch of <script type="text/javascript" src="somescript.js">
None of the JS code gets executed
Looks like you're loading the HTML from inside your bundle. This means that all the additional files (.js, .css, and any media files) also need to be present in your bundle. So the first thing to check is to look inside the contents of your executable and make sure the js, etc. files are included.
If that looks fine the next thing to check is if the html, js, or css files reference content via relative or absolute URLs. If there's an absolute path reference in the web content then UIWebView is going to try to download that content each time so it'll only work when you have a net connection. If the path is relative then it's going to look in the bundle to see if such a file exists.
When you included the html and content into the XCode project file you probably dragged the file(s) over to the project side-bar and were asked whether to "Recursively create groups for any added folders" or to "Create Folder References for any added folders."
The default is the first one which means XCode creates a yellow folder in your project, but it'll ignore the directory hierarchy on disk when time comes to generate the output bundle. If you choose the second option then the folder is blue and if you look in your output bundle you'll see that the whole folder hierarchy has been replicated.
The first works for simple web pages where everything is at the same folder level and you can use the method you list above to load it. The second case works better if your web page is complex and references content in sub-folders in which case you need to load the web pages from a relative path (say, the 'webpages' folder):
NSString *path = [[NSBundle mainBundle]
pathForResource:#"index" ofType:#"html"
inDirectory:#"webpages"];
The last thing to check for is if there are any BASE tags in the html file. This is a way to specify a default address or target for all links on a page, but it can muck up webview links.
The problem is that this call:
NSURL *url = [NSURL fileURLWithPath:path];
doesn't setup a baseURL and so relative paths in the .html file for things like javascript, css, images etc don't work.
Instead use this:
url = [NSURL URLWithString: [path lastPathComponent]
relativeToURL: [NSURL fileURLWithPath: [path stringByDeletingLastPathComponent]
isDirectory: YES]];
and then things like "styles.css" in the index.html file will be found IFF they are copied into the bundle next to the .html file.
You need to set this:
myWebView.dataDetectorTypes = UIDataDetectorTypeLink
Make sure that the .js files are in your copy to resource bundle section and not in the compile section. Xcode places them in the compile group by default.
When adding pathFor resource in Dictionary , it displays a nil string error.
My attempt was to run an entire web page out of the Xcode project file. To do that you must:
When importing the file select "Create folder references for any added folders".
Set up the web view, but make sure you set the relative path as previously mentioned.
NSString *path = [[NSBundle mainBundle] pathForResource:#"filename"
ofType:#"html"
inDirectory:#"Directory"];
NSURL *url = [NSURL URLWithString:[path lastPathComponent] relativeToURL:
[NSURL fileURLWithPath: [path stringByDeletingLastPathComponent]
isDirectory:YES]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.currentWebView loadRequest:request];

Is that possible to cache UIWebView in iPhone?

I was using NYTimes iPhone application, I become bit curious when I notice that it cache UIwebview even though I didn't open that article.Does anyone have idea how to do that?
How NYTimes iPhone application doing offline reading?
Any help greatly appreciated.
Thanks,
Amit
I wrote the NYTimes app. Here are some details that you could have gotten by looking inside the app bundle.
I download the HTML for the articles, strip out whatever unsupported HTML and JS crud the producers stuffed in it and cache it in the backing store.
The article content is contained in a series of P tags (HTML fragment). I stuff that into a special HTML skeleton page that ships with the app. The static wrapper page also contains CSS and JS used to properly display the article and lazily load the images.
The image loading is really cool. The web view is notified when the images are ready. layout is not affected because I already know the sizes of the missing images.
You can use UIWebView to load html files stored locally on the iPhone.
NYTimes app is probably caching html and images in local storage.
Search google for "UIWebView local" and you get several useful hits.
I tried it out and it works great:
First, create a "view based application" and add a UIWebView to the NIB.
Second, add this code to your UIViewController code:
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSURL *url = [NSURL fileURLWithPath:path isDirectory:NO];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
[super viewDidLoad];
}
Third, add a file called "index.html" to your "Resources" folder in xcode and it will be displayed.
UPDATE:
Indeed, the complicated part of this is downloading the images and stylesheets for the webpage. Doing this server side is easy with Simple HTML Parser (and PHP). Just package everything in a zip and download to your iPhone.
Alternatively, you could do it locally with a C/C++/OBJC HTML parser (libxml2.2 is available on iOS). See this SO question Parsing HTML on the iPhone.
It's going to a bit of a project, so good luck.