shouldStartLoadWithRequest doesn't run stringByEvaluatingJavaScriptFromString method - iphone

I am trying to run the code below in webview's shouldStartLoadWithRequest delegate method but it doesn't make any changes.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(#"webView shouldStartLoadingWithRequest");
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"document.execCommand('bold', false,null)"]];
return TRUE;
}
There is no error, it prints the NSLog and everything in the method works great except "stringByEvaluatingJavaScriptFromString" method.
But if I try to make text bold in another function, for example an IBAction method, works fine.
-(IBAction)boldClick:(id)sender
{
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"document.execCommand('bold', false,null)"]];
}
Actually, this is my company's special application and this UIWebView will not show the web pages. I am using it to show some custom HTML pages.
I need to make everything in "shouldStartLoadWithRequest" because I am trying to run an objective-c method from javascript.
UPDATE
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
// Break apart request URL
NSString *requestString = [[request URL] absoluteString];
NSArray *components = [requestString componentsSeparatedByString:#":"];
// Check for your protocol
if ([components count]==3)
{
[self makeBoldText];
return NO;
}
else
{
return TRUE;
}
}
-(void)makeBoldText
{
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"document.execCommand('bold', false,null)"]];
}

The doc says that method webView:shouldStartLoadWithRequest: is sent before a web view begins loading a frame. After you return YES in this method, web view starts loading a request. So any javascript you execute will have no effect, because a new page will be loaded after your JS call.
You can probably use webViewDidFinishLoad: method to execute javascript after page finishes to load. Or if you want to trigger JS by clicking on a link, you can use shouldStartLoadWithRequest but return NO from it.

Related

Capturing an specific logout URL in a webView to interact with an embedded html app

I have a simple app, with only a login page. When the user sing-in, I load a webview running an entire html5 app.
The point is that I would like to capture when the user has decided to logout.
Is there any way to listen when a specific url has been loaded?
If so, when the user press logout and the /logout.html would be loaded and I would have the opportunity of returning the control to the iOS app.
Conform to UIWebViewDelegate in your .h file
Set your delegate of UIWebview
Implement the below method:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"URL Request = %#",[[[webView request] URL] lastPathComponent]);
// if (check stringlogout.html) {
// return NO;
// }
// else{
// return YES;
// }
return YES;
}
Check your last component string if it is logout.html then you can do the required stuff and in else you can do what if it is not logout.html action.
Return YES if you want to load the page and return NO if you do not want to load the page.
Good luck with your app...

UIWebView shouldStartLoadWithRequest only fires once when calling a modal view from inside

I have part of my app written in JS and running inside of a WebView. I'm using the UIWebView shouldStartLoadWithRequest method to capture http requests as a means of communicating between JS and obj-c. This works great until I attempt to load a Modal View Controller over my webview from inside the shouldStartLoadWithRequest method. Once this happens, shouldStartLoadWithRequest is no longer called. Sometimes I need to dismiss this modal view controller and go back to the webview and do some things and then re-present the modal controller. The modal controller comes up the first time just fine, then I dismiss it and attempt to present it again by navigating to a URL from javascript and it no longer will present itself. NSLogs inside shouldStartLoadWithRequest are never run.
In my javascript I do something like this:
window.location='myapp:whateverMethod';
objective c code looks like this:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[request URL] absoluteString];
NSLog(#"REQUEST URL: %#", requestString);
if([requestString hasPrefix:#"myapp:"]) {
NSArray *components = [requestString componentsSeparatedByString:#":"];
NSString *function = [components objectAtIndex:1];
if([self respondsToSelector:NSSelectorFromString(function)]) {
[self performSelector:NSSelectorFromString(function)];
}
return NO;
}
return YES;
}
-(void) whateverMethod {
NSLog(#"whateverMethod called!");
// This is a quick way to grab my view controller from the storyboard, so assume it exists
UIViewController *splash = [self.storyboard instantiateViewControllerWithIdentifier:#"splashViewController"];
[self presentModalViewController:splash animated:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
[self dismissModalViewController:splash animated:NO];
});
}
At this point my webview is still visible. I navigate from page to page in my webapp and all javascript works great in it but the "shouldStartLoadWithRequest" delegate method of the webview no longer is called. I cannot figure out why. Does anyone have any ideas?
I noticed that Cordova doesn't set the window.location property. Instead it has two options: it either creates an iframe and sets the src of the iframe to that url, or it creates an XMLHttpRequest object e.g. in the iOSExec() function:
if (bridgeMode) {
execXhr = execXhr || new XMLHttpRequest();
// Changeing this to a GET will make the XHR reach the URIProtocol on 4.2.
// For some reason it still doesn't work though...
execXhr.open('HEAD', "file:///!gap_exec", true);
execXhr.setRequestHeader('vc', cordova.iOSVCAddr);
if (shouldBundleCommandJson()) {
execXhr.setRequestHeader('cmds', nativecomm());
}
execXhr.send(null);
} else {
execIframe = execIframe || createExecIframe();
execIframe.src = "gap://ready";
}
That being said, it may be beneficial to use something like Cordova instead of trying to roll it yourself (even if it's just embedding their view controller), since they handle a lot of the headaches that come up with webview delegates.
I've just had the same problem, but related to using a href="#" anchor.
This Stack Overflow answer sorted it
There are more answers on that thread that deal with widow.location, so you may have luck with them.
Checked out Cordova and they have their own queuing system, not really a help. But...
Disobedient Media's answer gave me an idea. Instead of window.location, why not try window.location.hash.
Now some JS code for logging is:
function sendMsgToNative(msg)
{
window.location.hash = '~cmd~' + msg;
}
console.log = function (msg) { sendMsgToNative('log~js ' + msg); };
and the Objective-C code is:
NSString *req = [request.URL absoluteString];
NSArray *components = [req componentsSeparatedByString:#"~"];
// Check for your protocol
if ([components count] > 1 && [(NSString *)[components objectAtIndex:1] isEqualToString:#"cmd"])
{
// Look for specific actions
if ([(NSString *)[components objectAtIndex:2] isEqualToString:#"log"])
{
NSString *logStr = [(NSString *)[components objectAtIndex:3] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
LOGI("%#", logStr);
}
}
You get the full URL including 'http:...' so I chose tilde instead of colon, and incremented the indices.
Now you can log all willy-nilly and send whatever amount of commands you want and they will all get through :-)
I (embarrassingly) spent a couple of hours working on this today, and realised that in my viewDidDisappear: I was setting the UIWebViewDelegate to nil!
All I needed to do to fix was once the modal was dismissed, re-set the UIWebViewDelegate and everything worked again.

Use a button on a HTML page to call xcode IBAction

This seems like a strange one. Not even sure if it is possible!!
I have a UIWebView that loads a local html page. On this html page I have a button.
I want to click on the button and then call an IBAction in Xcode.
How would I go about this? Or can you even do this???
Thanks guys,
Stefan.
You can do it by using a custom protocol. In you html file, you can link to something like myProtocol://callSomeAction.
Then on your UIWebViewDelegate (probably your UIViewController) you have to implement the method called:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
(Docs here)
The idea is that on that code, you detect the protocol based on the data in the request parameter. If it is myProtocol, you can call your IBAction and return NO. If it's something else, you fallback to have the web view load the page, and just return YES.
The code would look something like this:
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSString* scheme = [[request URL] scheme];
if ([#"myProtocol" isEqual:scheme]) {
// Call your method
return NO;
} else {
return YES;
}
}
Have the button on the HTML page open a custom url such as myapp://buttonclick.
In your web view delegate, implement webView:shouldStartLoadWithRequest:navigationType:. Check whether the request includes your custom URL and if it does, call any Obj-C method you want.
I have similar kind of situation, but its an imageclick , href and handling this method of webview,
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener{
NSString *host = [[request URL] host];
//if (host!=nil)
{
WebNavigationType eActionType = (WebNavigationType)[[actionInformation valueForKey:WebActionNavigationTypeKey] intValue];
NSURL *pOrignalURL;
if(eActionType == WebNavigationTypeLinkClicked)// == [actionInformation valueForKey:WebActionNavigationTypeKey])
{
/* we will handle it */
pOrignalURL = [actionInformation valueForKey:WebActionOriginalURLKey];
NSString *pElementName = [actionInformation valueForKey:WebActionElementKey];
if([[pOrignalURL absoluteString] hasPrefix:#"app:"]){
[listener ignore];
return;
}
}
//[[NSWorkspace sharedWorkspace] openURL:pOrignalURL];
NSArray* urls = [ NSArray arrayWithObject:
[ NSURL URLWithString:[pOrignalURL absoluteString]]];
[[ NSWorkspace sharedWorkspace ]
openURLs:urls
withAppBundleIdentifier:nil
/* use default system bindings */
options:NSWorkspaceLaunchWithoutActivation
additionalEventParamDescriptor:nil
launchIdentifiers:nil ];
/* default behavior */
[listener download];
return;
}
}

UIWebView loading parsed html string

i'm building an app that will display some newsletters to the users. the newsletters are displayed in a uiWebView. I'm reading the url's for the newsletters from an rss feed. I parse the xml, and in a table view i have all the newsletters. When a cell is clicked the uiWebView is pushed and the newsletter is loaded. Because the uiWebView doesn't open links that have target=_blank i need to replace the _blank from target with "". In an NSOperation i download the contents of the html and after the download is finished i replace the strings like this:
NSMutableString *page = [[[NSMutableString alloc] initWithData:pageData encoding:NSISOLatin1StringEncoding] autorelease]; [page replaceOccurrencesOfString:#"target=_blank" withString:#"target=""" options:0 range:NSMakeRange(0, [page length])];
after i do this i load the parsed string in the webView to display it to the user.
[myWebView loadHTMLString:page baseURL:[NSURL URLWithString:#""]];
while the NSOperation is downloading the contents of the page an HUD with a activity indicator and a label that says Loading is showed. When the download is finished the HUD is removed from the superview.
so far so good..but here come the questions. i used the NSOperation with the callback function because i wasn't able to determine the last call to webDidFinishLoading ( i read this UIWebView - How to identify the "last" webViewDidFinishLoad message? - this is a part of my problem but the response was to use some Apple private classes and that is a problem for me). so i'm doing this:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = request.URL;
NSString *urlString = url.absoluteString;
NSLog(#"WebViewSouldStartWithRequest: %#", urlString);
if (navigationType == UIWebViewNavigationTypeLinkClicked)
{
if(![urlString isEqualToString:#"about:blank"])
{
NSLog(#"FeedViewController: startPageLoad");
activityHud = [[HUDView alloc] initWithFrame:CGRectMake(110, 100, 80, 80)];
[activityHud setText:#"Loading"];
[myWebView addSubview:activityHud];
NSLog(#"FeedViewController: pageUrl = %#", urlString);
[self resetWebView];
[urlLoader loadPageFromUrl:request.URL withCallbackTarget:self withCallbackSelector:#selector(endLoading:)];
}
}
return NO;
}
- (void) endLoading:(NSMutableString *)page { [self resetWebView]; NSLog(#"FeedViewController: endLoading"); [activityHud removeFromSuperview]; [myWebView loadHTMLString:page baseURL:[NSURL URLWithString:#""]]; }
after i touch a cell in the table view and the newsletter is showed it looks like it should, when i click a link in the newsletter, the page is loaded with the new request, parsed but visually is not looking as it should (i looked at the page after parsing in NSLog and it has the css styles and html tags, head, body opened and closed correctly) anyone had this problem with uiWebView, not showing webpages correctly?
i tried loading the page with - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType{ return YES; }
and in - (void)webViewDidFinishLoad:(UIWebView *)webview {} and in - webViewDiStartLoad
but the methods are being called for every item that is loaded in the webview so showing and hiding the HUD in those method is not a good solution.
I encountered some problems while using - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { ... return NO }
for example links that have paths like this /bilder-galerien to not work and in NSLog i receive this for them
Unknown scheme, doing nothing:
/bilder-galerien
but when i use
(BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { return Yes } and didstartLoading and finishLoading the urls are loaded and i don't know why...
another problem are the special german characters..in my first load(after pressing a cell in the uiTableView) the page is parsed like it should be by the uiWebView but after i click a link and the corresponding request is loaded also the characters are not parsed correctly..
can anyone point me in the good direction? thank you in advance
UIWebView doesn't open target="_blank" links. Certain links don't fire UIWebViewNavigationTypeLinkClicked events. This happens when the link has the target="_blank" attribute.
To work around this problem i used the code below. It injects some javascript after the page load to remove the target attribute from all the links. After using this code i didn;t needed anymore to parse the html source and replace _blank with _self for example.
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *js = #"\
var d = document.getElementsByTagName('a');\
for (var i = 0; i < d.length; i++) {\
if (d[i].getAttribute('target') == '_blank') {\
d[i].removeAttribute('target');\
}\
}\
";
[webView stringByEvaluatingJavaScriptFromString:js];
}
I like SorinA answer, we can make it even better by:
avoid obstructing javascript with var d
as we know we're running webkit engine we can use selectors:
...so in your UIWebViewDelegate protocol method invoke js code:
- (void) webViewDidFinishLoad: (UIWebView *) webView {
[webView stringByEvaluatingJavaScriptFromString: #"(function($){for(var i=0;i<$.length;++i){$[i].removeAttribute('target')}})(document.querySelectorAll('a[target=_blank]'))"];
}

Disable hyperlinks in UIWebView

I want to disable hyperlinks in UIWebVIew after the initial page loaded without disabling the scrolling feature. That is, I should have user interaction enabled.
You can work with webView shouldStartLoadWithRequest like this:
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
NSURL *loadURL = [[request URL]retain];
//change next line to whatever condition you need, e.g.
//[[loadURL relativeString] ....] contains a certain substring
//or starts with certain letter or ...
if([[loadURL scheme] isEqualToString: #"file"])
{
[loadURL release];
return TRUE;
}
[loadURL release];
return FALSE;
}
You also have to set the webViews delegate an object of class where this method is implemented in: [webView setDelegate:my...];
By the implementation above, no url is loaded except those for which the condition is true. At least for the url of the first site it has to be true. The code above works for a web view initially loaded with contents of a file, containing only links to 'http://' or 'https://' or ...