UIWebview contents as NSString from HTML - iphone

I'm trying to reading part of HTML source code from website that has been encapsulated by javascript;basically i'm trying to read email which appears in webview but not in real source code using NSString method of 'NSString stringWithContentsOfURL:(NSURL *)url'. it looks like code showing up before hitting up the inner java script (which executes and shows the email address to show).
are there any way i can get into NSString the contents that I viewed over UIWebview?
I tried to use the method 'webView stringByEvaluatingJavaScriptFromString' it worked for only displaying through webview browser didn't return any string value.
Are there anyway I can get the string?

This should work in the general case:
NSString *markup = [webView stringByEvaluatingJavaScriptFromString:
#"document.getElementsByTagName('html')[0].outerHTML"];
Just make sure the page is loaded before you call it.

Related

text and image as one scrollable entity

I need an undefined amount of text and one or more pictures to be scrollable as one entity. I'm quite surprised that this doesn't seem to be provided by default, I thought I've seen that several times before... I tried to google, but all I find doesn't fit. The images won't be wider than the screen, but in between lines of text.
I need something that let's me do something like:
image
textA
textA goes on
__ screen ends here, content goes on
textA goes on
textA goes on
image
image
textB
textB goes on
image
textC
The content for the text would come out of a plist, but I THINK I can predict it will be REALLY static, so I could just set the Text in IB and create a view for every content -.-.
I've read about Web View, but as far as I got it, you'd need internet connection to make that work, and the app should work without any internet connection at all.
Any suggestions or experiences concerning that?
Thanks a lot!
There different way to get things done:
If you have static content and want a complicated layout and know how to do it in html you should go with UIWebview and a bundled html file and images and load it with something like:[webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"MyStuff" ofType:#"html"]]]];
You can also do a layout with UILabels and UIImageviews and arrange all of this onto one UIScrollview
If it's more dynamic you should go with the latter, but you need to program sort of layout algorithms that handle different number/sizes of images, number/length of test paragraphs and so on.
One option, as you mention, is UIWebView.
It does not require a connection, since you can load a static HTML into it executing:
– loadHTMLString:baseURL:
By specifying a baseURL that "points" to your bundle, you can also include images as resources in your Xcode project and have them displayed (by using <img src=... /img in your HTML):
NSString* basePath = [[NSBundle mainBundle] bundlePath];
[_label loadHTMLString:text baseURL:[NSURL fileURLWithPath:basePath]];
You don't need an internet connection to make a web view work. Look at this method on UIWebView:
- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL

Trying to load various local HTML files but the same one file always shows

I have an app that uses a Table View to show a list of stories a user can read and when they tap on a particular title a Detail View will open up and the story is displayed.
To begin with I had the app loading up the stories directly from the web and this worked perfectly. I used an array and to pass the details of the particular stories web address and used the following to load up the page
[detailWebView loadRequest:[NSURLRequest requestWithURL:detailURL]];
Now I want to load up files locally instead and from searching around I found the following
[detailWebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:detailURL ofType:#"html"]isDirectory:NO]]];
and it does load up my local 'Story 1' HTML file but that same HTML file gets loaded up regardless of the file name being passed by detailURL in my Table View which takes the format of
[bookOne addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Story One Title",#"name",
#"Story 1",#"url",nil]];
If I pass it a file name that doesn't exit the program quits so I'm pretty sure the different file names are being passed but the same HTML page always shows.
I've tried reboots etc but the same file always opens up, your help would be really appreciated.
Thanks
Kieron
Try to load the files with
loadHTMLString:baseURL:
it might be that NSURLRequest caches the file/response.
A different solution would to create an instance of the NSURLRequest with initWithURL:cachePolicy:timeoutInterval: and pass NSURLRequestReloadIgnoringLocalAndRemoteCacheData for the cachepolicy
Thanks Nick, while loadHTMLString didn't provide the answer it got me thinking differently and on the right track as to the real cause of the problem.
When using a web address in the first example detailURL had been set as NSURL and when I changed it to NSString my code worked fine. I don't understand why it would cause the loading of the HTML file(s) to act so weirdly but everything is running fine now, the different stories all load up as there should and there are no caching issues of any sort.

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];

UIApplication OpenUrl double-escaping my URLs

Basically, i'm trying to program a "tweet this" button from inside my application. Depending on their spot in the application, they can click the tweet button and it'll shoot them out to Safari with a tweet message that changes depending on where they are.
In order to create URLs, I have to escape the query string that I want to put in the NSUrl object. So, I do this:
NSString* escapedTweet = [#"Some String With Spaces" stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
After concatenating the base, my url comes out "http://www.twitter.com/home/?status=Some&20String%20With%20Spaces" - looked at it in the debugger and this is definitely the value (as expected). Now, I create my URL and launch safari:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:escapedUrlString]];
But here's where the issue comes up: OpenUrl appears to be escaping my percent signs, so the actual URL that Safari goes to is "http://www.twitter.com/home/?status=Some%2520String%2520With%2520Spaces", which is obviously a problem since twitter creates the status message as "Some%20String%20With%20Spaces".
NSUrl will NOT allow me to create a URL with spaces in it, so i'm completely lost as to how to get my URLs to just include a %20. Has anyone else run into this issue or found a workaround?
I'm running on an iPad with an up to date OS, not sure if it's the same issue on the iPhone.
Edit: In a nutshell, how do I get openUrl to open http://www.twitter.com/home/?status=Some%20Url%20With%20Spaces without escaping my percent signs and creating a URL like http://www.twitter.com/home/?status=Some%2520Url%2520With%2520Spaces?
I am assuming that escapedUrlString is declared as NSURL *escapedUrlString = [NSURL URLWithString:escapedTweet];. Then your problem probably lies in openURL:[NSURL URLWithString:escapedUrlString]];, because you’re taking an URL, passing it into another URL and then opening it. Fix by passing in escapedURLString (which should be named ‘escapedURL’) instead of URLWithString:….

Using a UIWebView with loadHTMLString/loadData breaks the back and forward buttons, workaround?

There's a known problem with embedded UIWebViews that if you load data into them using loadHTMLString or loadData, the canGoBack/canGoForward properties and goBack/goForward methods don't work. These only work when using loadRequest.
Since Safari's normal app cache doesn't work in embedded UIWebViews, creating a native app that effectively caches otherwise live content becomes impossible/unusable. That is, I can cache the contents of the HTML, Javascript, images, etc. and load them via loadHTMLString or loadData, but then the back and forward buttons don't work.
I could also use loadRequest and specify a file URL, but that breaks when it comes to communicating with the live site -- even if I specify a tag (because of cookie domain issues).
I have a work around that involves basically re-implementing the app cache using local store (and not having the native app do any caching itself), which is OK, but not really ideal. Are there any other work arounds/something I missed?
I am using the UIWebView's canGoBack to check to see if I'm at the first page of the history. If I am then I just call the little method I used to load the first page (displayLocalResource sets up the HTMLString and loads it into the webView). Here is a snippet:
//Implementing a back button
- (void)backOne:(id)sender{
if ([webView canGoBack]) {
// There's a valid webpage to go back to, so go there
[webView goBack];
} else {
// You've reached the end of the line, so reload your own data
[self displayLocalResource];
}
}
So do you download the HTML yourself, then pass it to UIWebView as a string? Why so? Do you modify it on the fly or something?
Maybe a custom URL schema would help? You use loadRequest with a schema of your own, which in turn works with HTTP and then feeds the webview whatever data you want?
I had a same problem. I tried manage the history, but it is error prone. Now I have discovered a better solution of this.
What you want to do is simply add a loadRequest to about:blank and make that as a placeholder for you before you call loadHTMLString/loadData. Then you are totally free from monitoring the history. The webview.canGoBack and canGoForward will just work. Of course, you will need a hack to handle go back to the placeholder about:blank. You can do that in webViewDidFinishLoad. Here is the code highlight:
In the function when you call loadHTMLString:
[weakSelf.fbWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
[weakSelf.fbWebView loadHTMLString:stringResponse baseURL:url];
Code to handle goBack:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if ([webView.request.URL.absoluteString isEqualToString:#"about:blank"]
&& ![webView canGoBack] && [webView canGoForward]) {
[weakSelf.fbWebView loadHTMLString:stringResponse baseURL:url];
}
}
I think it is also possible expand this solution to handle those loadHTMLString that is not the first load. Just by having a Stack to record all the string response and insert an about:blank on each loadHTMLString. And pop the stack when each time go back to about:blank.
Could you fetch the content, save it to the local filesystem, point the webview to the local filesystem using file:// URLs, then intercept the link follows with shouldStartLoadWithRequest to fetch more to local fs, point webview at new local content, etc?
I've had good luck with UIWebView and file:/// URLs. Basically you'd be intercepting load requests, fetching stuff yourself, writing it to the local filesystem with rewritten URLs, then loading that into the browser.
There seems to be no way to load/save the browser history.
Loading the string into a temp file and using that as a URL request seems to cure this. It's something about loading the string directly that causes UIWebView not to see it as the home page you can navigate back to. This code worked for me:
//If you load the string like this, then "webView.canGoBack" never returns YES. It's documented frequently on the web.
//Loading the string as a URL instead seems to work better.
//[self.myWebView loadHTMLString:str baseURL:nil];
//write the string to a temp file
NSString *fileName = #"homepage.html";
NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[data writeToURL:fileURL atomically:NO];
//open that temp file in the UIWebView
[self.myWebView loadRequest:[NSURLRequest requestWithURL:fileURL]];
Use this to enable/disable the back button:
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//this is to check if we're back at the root page.
if (webView.canGoBack == YES) {
self.backButton.enabled=YES;
}
else {
self.backButton.enabled=NO;
}
}