webViewDidStartLoad Request URL Not Set to URL Being Loaded - iphone

I am working on a browser that has back and forward buttons, that when tapped, should immediately display the URL of the page being loaded. Currently I do something like [webView goBack]; and then implement the delegate UIWebView method:
- (void)webViewDidStartLoad:(UIWebView *)aWebView {
NSLog(#"began loading URL: %#", aWebView.request.URL);
}
But this doesn't result in the behavior I expect.
For example, if I start out at www.google.com and then navigate to www.aol.com and then press the back button causing the web view's goBack code to be called, the NSLog will output began loading URL: http://www.aol.com/ even though it should state that google.com is being loaded. The google page will load perfectly however in the web view. What I don't understand is why the URL isn't consistent with what is being loaded. On occasion the log will spit out that aol.com is being loaded, followed by google.com, but this only happens sometimes. Is this a bug with the SDK or am I missing something?
Thanks in advance.

try in this delegate method
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"Request Url:%d",request.URL.absoluteURL);
}

I suggest to use an array of string every time you load a new url you add it in the array and beside it define a global counter increase/deacrease based on back/forward click so you can get the url easy like this
NSString *urlString = [urlArray objectAtIndex:theCounterValue];

try:
NSString *currentURL;
currentURL = [[NSString alloc] initWithFormat:#"%#", webView.request.mainDocumentURL];

Related

UIWebView Still load the URL even though returning NO

I am handling a UIWebView so that i can control which URLs should be loaded within or not, but some how even though it is retuning the NO , it still load the page. Although documentation clearly says that if you return NO, the UIWebView wont load the page.
When i debug,i can see it is returning NO but still UIWebView does load the URL.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"%#", [[request URL] absoluteString]);
NSString *fullURL = [[request URL] absoluteString];
NSRange range = [fullURL rangeOfString:#"#"];
if (range.length != 0) {
NSLog(#"We need to show the other view");
return NO;
}
return YES;
}
I solved the issue, documenting here so it may help someone else. Actually, the HTML, which we were loading using some javascript which was causing this issue. I found out by just using few plain html and testing with them. Once , we know the HTML is issue, we fixed the html and its working now.
Set a breakpoint or NSLog right before the return YES part. Maybe your method gets called twice for whatever reason, and it returns NO on one, and YES on the other.
First, make sure that you are setting the delegate in viewDidLoad with
webView.delegate = self;
(take care of not setting it 2 times, in a xib file and in viewDidLoad, it has caused me problems before)
Make sure you implement the UIWebViewDelegate in your class, something like this:
#interface RootViewController : UIViewController<UIwebViewDelegate>
Assuming you have taken care of all this and still you face problems. Also since you debugged and are SURE that the delegate method is returning NO. One reason I can think of as to why this is happening is that you are not loading a new page but using something like AJAX.
I tested the following code on 2 kinds of pages:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"THE loadCount is %d", self.loadCount);
if (self.loadCount > 1){
return NO;
}
self.loadCount++;
return YES;
}
Case 1: A webpage where every URL loads a new page. The above code works in this case, and I cannot load any pages after the first load, as required.
Case 2: A webpage in which the first load is a complete new web page. But everything else is loaded with AJAX, in that case, my loadCount does not increase and the pages load fine.
That's all I can think of with the data provided. :)
Same issue here, just to extend the answer above be careful when using rails 4 as backend because turbolinks add javascript to every link and then you can get wrong behavior on your delegate, happened to me returning NO on shouldStartLoadWithRequest and still see the request on my server.

Call 'action' on HTML web form in a UIWebView

I'm trying to skip the login page of a website, so I can give the user a more aesthetically appealing login page, and save their login credentials for automatic login in the future. So far I have two UITextFields, and a UIButton. The user enters their username and password, and upon pressing the UIButton their details are entered into the actual webform on the sites login page, and the subsequent 'logged-in' page is shown in a UIWebView. The webView will be hidden while the app logs the user in, and then presented when the 'logged-in' page has loaded.
I've managed to populate the webform, but simply can't actually submit it! I'm sure there's a very simple solution!
This is the code I have so far, and if there's a better way I should be doing this let me know!
-(IBAction)login:(id)sender
{
// Create js strings
NSString *loadUsernameJS = [NSString stringWithFormat:#"var username = document.getElementsByName('username')[0]; username.value='%#';", username.text];
NSString *loadPasswordJS = [NSString stringWithFormat:#"var password = document.getElementsByName('password')[0]; password.value='%#';", password.text];
// Autofill the form - this works, and I can see the values being put into the webform
[self.webView stringByEvaluatingJavaScriptFromString: loadUsernameJS];
[self.webView stringByEvaluatingJavaScriptFromString: loadPasswordJS];
// This is the method I have tried to submit the form, but nothing happens (ct100 is the ID of the form).
[self.webView stringByEvaluatingJavaScriptFromString:#"document.ct100.submit();"];
}
Below Approach might be helpful to you
Put this code inside the didFinishLoading Method Of UIWebView Protocol
Fill The USerId
[webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('username').value = 'userId'"];
Fill The Password
[webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('password').value = 'password'"];
Call Submit Button as below
[webView
stringByEvaluatingJavaScriptFromString:#"document.forms['login_form'].submit();"];
Herelogin_form is the Name of You Form
Note: You have to manage one thing once you have filled the Credential clicked the Submit button. you need to make some BOOL or something else for avoiding the loading repetition of UIWebView.
Try:
[webView stringByEvaluatingJavaScriptFromString:#"document.getElementById(\"ct100\").submit()"];
Your view controller needs to implement the UIWebViewDelegate protocol and set itself as the UIWebView's delegate. Returning YES from webView:shouldStartLoadWithRequest (see code below) will allow the web view to navigate to any url the your form is submitting to.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"Allow navigation to request = %#", request);
return YES;
}

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

Multiple UIWebViewNavigationTypeBackForward not only false but make inferring the actual url impossible

I have a UIWebView and a UITextField for the url. Naturally, I want the textField to always show the current document url. This works fine for urls directly input in the field, but I also have some buttons attached to the view for reload, back, and forward.
So I've added all the UIWebViewDelegate methods to my controller, so it can listen to whenever the webView navigates and change the url in the textField as needed.
Here's how I'm using the shouldStartLoadWithRequest: method:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"navigated via %d", navigationType);
//loads the user cares about
if ( navigationType == UIWebViewNavigationTypeLinkClicked
|| navigationType == UIWebViewNavigationTypeBackForward ) {
//URL setting
[self setUrlQuietly:request.URL];
}
return YES;
}
Now, my problem here is that an actual click will generate a single navigation of type "LinkClicked" followed by a dozen type "Other" (redirects and ad loads I assume), which gets handled correctly by the code, but a back/forward action will generate all its requests as back/forward requests.
In other words, a click calls setUrlQuietly: once, but a back/forward calls it multiple times.
I am trying to use this method to determine if the user actually initiated the action (and I'd like to catch page redirects too). But if the method has no way of distinguishing between an actual "back" and a "load initiated as a result of a back", how can I make this assessment?
Without this, I am completely stumped as to how I can only show the actual url and not intermediate urls. Thank you!
Ok here's what I ended up doing - not sure if this is the expected solution though. Scratch all the code from the above method and instead do this:
- (void)webViewDidStartLoad:(UIWebView *)webView {
NSString *urlString = [self.webView stringByEvaluatingJavaScriptFromString:#"document.URL"]
[self setUrlQuietly:[NSURL URLWithString:urlString]];
}
This means there's a slight delay between clicking a link etc and seeing the url show up in the textField, but it also guarantees that the textField always shows the document's actual title.
I'm not seeing the UIWebViewNavigationTypeBackForward event at all. And sometimes webViewDidStartLoad never gets called. Perhaps there have been some changes in iOS 5.
Anyway, here's my solution for keeping the title and URL up to date. Works very consistently, though the response could be a little faster.
- (void)webViewDidFinishLoad:(UIWebView *)wv
{
[self updateButtons];
titleLabel.text = [wv stringByEvaluatingJavaScriptFromString:#"document.title"];
NSURL *url = wv.request.URL;
[selectedUrl release];
selectedUrl = [url retain];
}

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.