UIWebView loading progress and adjust web page to fit the view page? - iphone

I am using UIWebView to load a web page.
There are 3 questions:
1.It it possible to track the percentage progress when UIWebView is loading the page?
2.I noticed that when Safari loading a web page, the URL textfield displays a blue background progress indicator to tell user the percentage of loading a web page. What is the technology for this?
3.I know there is property scalesPageToFit
scalesPageToFit
A Boolean value determining whether the webpage scales to fit the view and the user can change the scale.
I try to set it to YES, but it looks like that it is not in public API and my app stopped with black screen, I am not sure what is wrong?

To answer #1)
Instead of using a UIWebView, you can pull the webpage down as an NSData object using an NSURLConnection. When you get the initial response from your request from
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
the webserver should return a value of "expected content size" (which should be included in the response). Then you will keep getting the following method called each time you receive data:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
Keep appending the data to an existing NSMutableData object. Then you can check the size of your current data object (NSMutableData.length) against the expected response size.
percentage = (myData.length*100)/theResponse.expectedContentSize;
Then you can update a progress bar with that percentage! When
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
runs, use your data to call
[myWebView loadData:myData MIMEType:myMimeType textEncodingName:myEncoding baseURL:baseURL];
and it will load everything you pulled down into your web view.

Re #3:
You can try specifying scalePagesToFit as a YES in the viewDidLoad event of the UIView that contains your webview, e.g:
- (void)viewDidLoad {
self.webView.scalesPageToFit = YES;
//other code here...
}
For cases where this doesn't work, refer to the following StackOverflow question: UIWebView does not scale content to fit where the asker (and subsequently, answerer) gave this solution.
Apparently you can use javascript to resize your page to fit the browser:
NSString *jsCommand = [NSString stringWithFormat:#"document.body.style.zoom = 1.5;"];
[webLookupView stringByEvaluatingJavaScriptFromString:jsCommand];

You can try to use this subclass of UIWebView which uses private UIWebView methods - therefore, this solution is not 100% AppStore safe (though some apps do almost 100% use it: Facebook, Google app, ...).
https://github.com/petr-inmite/imtwebview

Re #2: I believe Safari uses a private API call on UITextField (so you can't do it if you want to submit to the app store), but you should be able to implement this yourself by putting a textfield with no border over the top of a progress bar.

To answer question #1, there is a solution that is App Store safe, and uses a similar method as Webkit to track progress.
https://github.com/otium/OTMWebView

Related

HTTP with username password authentication and XML parsing iPhone

I want to download data from HTTP using username and password for authentication using objective-c. And want to download the data which will be returned. And want to parse it for usage, like if the data gets downloaded successfully a new screen will be opened and if data could not be downloaded it would take me to another screen.
Please let me know if there is any full tutorial related to this or let me know how can I do this .. ?
thanks in advance
You should definitely check out Apple's SeismicXML code, it is really simple to follow and uses classes native to apple. In this example, an RSS feed is downloaded and displayed by parsing the XML. I am using this modified code to query Amazon and download product information. Check it out here:
http://developer.apple.com/library/ios/#samplecode/SeismicXML/Listings/ReadMe_txt.html
I hope this helps!
Use the connection delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// To get into a new view controller. Push the newviewcontroller with the help of navigation controller.
}
and use the parsing delegate
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
//To use the parsing data to be displayed in other view controller.Push the Otherviewcontroller with the help of navigation controller.
}

UIWebView: open some links in Safari, some within the view

My app features content that (for text formatting reasons) is presented in an UIWebView. Within the content there are links, some of which should open their target in mobile Safari, while others should navigate within the content.
So far, I've catched the link requests using a UIWebView delegate. In my implementation of
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
I'd check the requests URL using lastPathComponent or pathComponents for known elements to determine whether to open the link externally or within the view.
However, I just found out said methods are only available since iOS 4.0, which would render the app useless on iPad. Plus I have the feeling I'm using a dirty solution here.
Is there another way to somehow "mark" the links within my content in a way that makes them easy to distinguish later, when processing the request in the delegate method?
Thanks alot!!
You could covert the URL request into a string, and do a compare for a subdirectory on your website, such as in URLs that only start with "http://www.sample.com/myapp/myappswebcontent/", against the initial substring of your URL. Anything else, send to Safari.
You should set a policy delegate of web view:
For instance in the controller, that contains a web view
[webView setPolicyDelegate:self];
and then override a decidePolicyForNavigation method (this is just an example):
- (void)webView:(WebView *)sender decidePolicyForNavigationAction: (NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id <WebPolicyDecisionListener>)listener
{
if ([[actionInformation objectForKey:WebActionNavigationTypeKey] intValue] == WebNavigationTypeLinkClicked) {
[listener ignore];
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
}
else
[listener use];
}
you can distinguish there kind of link and ignore or use the listener. If you ignore it, you can open the link in safari, if you use it, the link will open in your webview.
HTH

Reused UIWebView showing previous loaded content for a brief second on iPhone

In one of my apps I reuse a webview. Each time the user enters a certain view on reload cached data to the webview using the method :-
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
and I wait for the callback call
- (void) webViewDidFinishLoad:(UIWebView *)webView.
In the mean time I hide the webview and show a 'loading' label.
Only when I receive webViewDidFinishLoad do I show the webview.
Many times what happens I see the previous data that was loaded to the webview for a brief second before the new data I loaded kicks in.
I already added a delay of 0.2 seconds before showing the webview but it didn't help.
Instead of solving this by adding more time to the delay does anyone know how to solve this issue or maybe clear old data from a webview without release and allocating it every time?
Thanks malaki1974, in my case I wasn't using a modal view.
When I sat with an Apple engineer on WWDC 2010 and asked him this question his answer was simply: "Don't reuse UIWebViews, that's not how they were ment to be used."
Since then I make sure to calls this set of lines before allocating a new UIWebView
[self.myWebView removeFromSuperview];
self.myWebView.delegate = nil;
[self.myWebView stopLoading];
[self.myWebView release];
That solved the issue.
Clear the contents of the webview before you try to load new content
[self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
First, the UIWebView renders it contents in a background thread. Even when you receive webViewDidFinishLoad: it might not be completely done. Specially if it is an ajax-intense page that comes from the network.
You say you are hiding the view. I wonder if that means that the webview delays its drawing completely. What you could try is to move the UIWebView offscreen or obscure it with another view. Maybe that will change it's drawing behaviour.
If you do not need an interactive UIWebView then you can also consider to do it completely offscreen in a separate UIWindow and then create an image from that UIWebView's layer.
That's what I do, and it works:
[_webView stringByEvaluatingJavaScriptFromString:#"document.open();document.close();"];
Try loading a local file that is blank or has a loading graphic when you hide it, rather than just loading new content when you show it. Since the file is local it will be quick and even if the new page takes a while to load it will have either blank or loading expected behavior.
If you got controll over the html. You can communicate back to objective-c when the document is ready. Like so in jQuery:
function messageNative (name, string) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "appscheme://" + name + "/" + string);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
$(function() {
messageNative('webview', 'ready');
});
And then in UIWebView's delegate method webView:shouldStartLoadWithRequest:navigationType: wait for the request with url equal to "appscheme://webview/ready". Then you should know: the document is loaded and ready for display. Then all that is missing is a simple fade-in or something like that :)

UIWebView didFinishLoading fires multiple times

I have some code that needs to run after the a UIWebView finishes loading a document. For that I've set the UIWebView's delegate to my controller, and implemented the webViewDidFinishLoading method.
This gets called multiple times, depending on the type of page to load. I'm not sure if it's because of ajax requests, requests for images, or maybe even iframes.
Is there a way to tell that the main request has finished, meaning the HTML is completely loaded?
Or perhaps delay my code from firing until all of those events are done firing?
You can do something like this to check when loading is finished. Because you can have a lot of content on the same page you need it.
- (void)webViewDidFinishLoad:(UIWebView *)webview {
if (webview.isLoading)
return;
// do some work
}
It could be enlightening (if you haven't gone this far yet) to NSLog a trace of load starts and finishes.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"Loading: %#", [request URL]);
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"didFinish: %#; stillLoading: %#", [[webView request]URL],
(webView.loading?#"YES":#"NO"));
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"didFail: %#; stillLoading: %#", [[webView request]URL],
(webView.loading?#"YES":#"NO"));
}
I just watched the calls to all three in one of my projects which loads a help page from my bundle and contains embedded resources (external css, YUI!, images). The only request that comes through is the initial page load, shouldStartLoadWithRequest isn't called for any of the dependencies. So it is curious why your didFinishLoad is called multiple times.
Perhaps what you're seeing is due to redirects, or as mentioned, ajax calls within a loaded page. But you at least should be able balance calls to shouldStartLoad and either of the other two delegate functions and be able to determine when the loading is finished.
Check this one it so simply and easy way to achieve no need to write too much code:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([[webView stringByEvaluatingJavaScriptFromString:#"document.readyState"] isEqualToString:#"complete"]) {
// UIWebView object has fully loaded.
}
}
This question is already solved, but I see it lacks an answer that actually explains why multiple calls to webViewDidFinishLoad are actually expected behavior
The aforementioned method is called every time the webview finishes loading a frame. From the UIWebViewDelegate protocol documentation:
webViewDidFinishLoad:
Sent after a web view finishes loading a frame.
In fact, this is also true for all the other methods that comprise the UIWebViewDelegate protocol.
Try this it will work fine
-(void)webViewDidFinishLoad:(UIWebView *)webview
{
if (webview.isLoading)
return;
else
{
// Use the code here which ever you need to run after webview loaded
}
}
This happens because the callback method is called every time a frame is done loading. In order to prevent this set the "suppressesIncrementalRendering" property of the webview to true. this will prevent the webview from rendering until the entire data is loaded into the memory. This did the trick for me
I have notice something similar and it was a confusion: I have a UITabBarController, it seems to preload all ViewControllers linked to its tabs on launching the App (in spite of showing just first_Tab_ViewController), so when several tabs have ViewController with WebView their respective webViewDidFinishLoad are called and if I have copied pasted:
NSLog(#"size width %0.0f height %0.0f", fitingSize.width, fittingSize.height);
in several, I get several output in console that appears to be a double calling when they really are single calling in two different UIWebViews.
You could check the loading and request properties in the webViewDidFinishLoad method
Possibly related to this issue is a property on UIWebView introduced in iOS6: suppressesIncrementalRendering.

UIWebView not responsive

I'm having trouble intercepting URL clicks within a UIWebView. I've read around and found the most common problem is that links have a target="_blank" attribute to open in a new window, but mine have no such attribute. URLs are of the form "/word", where word is some arbitrary word. I'm also encoding them with %20 when necessary. My UIWebViewDelegate class doesn't even receive a shouldStartLoadWithRequest: event, just nothing happens. I've also tried inserting a button into the HTML, and these are unresponsive too.
Are there any other rules governing which URLs UIWebView will acknowledge? I've tried using an absolute URL (and even just "http://www.google.com"), and also replacing the entire string of HTML with just a valid link to google, but all to no avail. And the really odd thing is that I can find one link that will work - it can be clicked and I successfully catch the event, but there seems (I've looked pretty hard) to be nothing different between the HTML containing this link and the others.
Given that I'm new to the iPhone platform, I figure I may very well be doing something more fundamentally wrong. Flow through the app looks like this: the main view is a UITableViewController, which lets users select from a list of words. When a word is selected, a new UIViewController is created and pushed onto the screen. This is the view with a UIWebView, which displays data loaded from a SQLite database. Some relevant bits of code are below.
//move from the UITableViewController to UIViewController with UIWebView
DetailViewController *viewController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
...
[self.navigationController pushViewController:viewController animated:YES];
//load HTML and display it in a UIWebView
NSString *output = [NSString stringWithFormat:#"%#%#%#",header,definition,footer];
[definitionView loadHTMLString:output baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
Any help would be greatly appreciated - been pulling my hair out for a while with this one.
You should take a look at PhoneGap: http://phonegap.com/
PhoneGap does exactly this (basically it's a big giant UIWebViewDelegate that makes it possible to write JavaScript apps for the iPhone that are able to take advantage of the camera, GPS, accelerometer etc.). Download the source, open PhoneGap.xcodeproj in the 'iphone' folder, and take a look at class PhoneGapDelegate. This is a great example usage of UIWebViewDelegate, hard to find a better example :) And it works of course.