Can a UIWebView interact (communicate) with the app? - ios

I went to use UIWebView to display dynamic content, instead of doing it natively using UI-elements. Is it possible to trigger native app functions from simply hitting links inside the UIWebView? Example: hitting a link which then switches current View?

Yes, it's possible. In your html, you write a JS to load a URL with a fake scheme such as
window.location = "request_for_action://anything/that/is/a/valid/url/can/go/here";
Then, in your iOS code, assign a delegate to your webView, and in your delegate, handle
webView:shouldLoadWithRequest:navigationType
with something like
if( [request.URL.scheme isEqualToString: #"request_for_action"] )
{
// parse your custom URL to extract parameter, use URL parts or query string as you like
return NO; // return NO, so webView won't actually try to load this fake request
}
--
Just an aside, you can do the other way, let iOS code invoke some JS codes in your html by
using
NSString* returnValue = [self.webView stringByEvaluatingJavaScriptFromString: "someJSFunction()"];

Yes! When the user presses a link, you hear about it in the web view's delegate and can then do whatever you want. Powerful stuff can be done this way.
The web view's delegate is sent webView:shouldStartLoadWithRequest:navigationType:. You analyze what happened, and respond as you wish. To prevent the web view from trying to follow the link (which may be completely fake, after all), just return NO.
In this example from the TidBITS News app, I have a link in the Web page that uses a totally made-up play: scheme. I detect that in the delegate and play:
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)r
navigationType:(UIWebViewNavigationType)nt {
if ([r.URL.scheme isEqualToString: #"play"]) {
[self doPlay:nil];
return NO;
}
if (nt == UIWebViewNavigationTypeLinkClicked) {
[[UIApplication sharedApplication] openURL:r.URL];
return NO;
}
return YES;
}

Implement the UIWebViewDelegate method webView:shouldStartLoadWithRequest:navigationType:.
Handle navigationType and the request as needed.

Related

Can we use HTML code in SWIFT?

I have some attractive graph which is made in HTML and javascript.
So my questions are:
Can I use this work in my swift app,
Can we use javascript and html code to make graph in app?using webview
Can we pass values from swift/objective c code to javascript/html code and vice-versa?
If yes , How can we pass variable value to html/javascript and how to to accept this value in HTML
html text can be embedded into labels as NSAttributedString instances
HTML content that has scripts and stuff can be put into a web view container: UIWebView or WKWebView
YES. Create a webview then load this html page.
YES. (same as answer 1)
YES .
To send to html page: call [webView stringByEvaluatingJavaScriptFromString:function];
To send to swift/objective-c : handle shouldStartLoadWithRequest of your UIwebview.
-(BOOL) webView : (UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *) request navigationType : (UIWebViewNavigationType)navigationType {
if ([[[request URL] absoluteString] hasPrefix:#"yourPrefixe:"]) {
//do your works
//then cancel current request
return NO;
}
return YES;
}

Can I create a link from within my webview browser that will call a specific function

We are using the native geolocation data in the address string we use when we create a webview component (eg http://example.com?long=36.333&latt=-143.222). The reason we are doing this is that we want to check location but don't want to use the browser location facility which pops up an alert each day asking if we can use your location.
Ideally I want to be able to create a new webview component (with new location data) in response to a click on a link within the webview component. I know we can call the app from a link (eg myappname://) but can I add parameters to this call to trigger certain events such as a function that we reload the webview component?
If it cant be done from a link within the html, I assume I will have to place a native button on the page and call it from there.
You can do. First of all, set link like http://example.com?#SetCustomLocation in your HTML content.
Set WebView Delegate method like bellow :
-(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType
{
if ( inType == UIWebViewNavigationTypeLinkClicked )
{
NSString *url = [NSString stringWithFormat:#"%#", [inRequest URL]];
if([url hasSuffix:#"#SetCustomLocation"])
{
NSString *strURL = #"http://example.com?long=36.333&latt=-143.222"; //Set Custom URL with Lat/long Whatever you need
NSURL *cURL = [NSURL URLWithString:[strURL tringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[[UIApplication sharedApplication] openURL:cURL];
}
}
return YES;
}
Regards.

How can I open new pages in new UIWebViews?

I'd like to open new web pages in new UIWebViews. For example, when you go to Google that would count as one web page, but when you click on Google Images within Google it would count as a new web page.
All new web pages will be loaded in new UIWebViews and will be put in a History array that contains all the UIWebViews.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if (navigationType == UIWebViewNavigationTypeBackForward) {
// The UIWebView already exists for that page in the history array, call it and display it.
}
else if (navigationType == UIWebViewNavigationTypeFormResubmitted) {
// No problem, load it in the current UIWebView.
}
else if (navigationType == UIWebViewNavigationTypeFormSubmitted) {
// It's a new page, load it in a new UIWebView.
}
else if (navigationType == UIWebViewNavigationTypeLinkClicked) {
// It's a new page, load it in a new UIWebView
}
else if (navigationType == UIWebViewNavigationTypeReload) {
// Just reload the current UIWebView.
}
else if (navigationType == UIWebViewNavigationTypeOther) {
// There's no way this is a new page. Do nothing.
}
}
As you probably have guessed, it didn't work. All you have to do is follow this path:
Go to http://www.9gag.com
Click the share on twitter icon under any post.
The link to share on twitter will look something like this:
https://twitter.com/intent/tweet?original_referer=http%3A%2F%2F9gag.com%2Fgag%2FaRQpqj2%3Fref%3Dt.mw&source=tweetbutton&text=I%20don%27t%20throw%20the%20word%20%27genius%27%20around%20loosely%2C%20but...&url=http%3A%2F%2F9gag.com%2Fgag%2FaRQpqj2%3Fref%3Dt.mw&via=9GAG
Although, someone would expect this link to be of type LinkClicked because you clicked the twitter icon to go there, it's actually of type Other, any desktop web browser will consider this link to be a pop up and would have opened it in a new window. Safari on iOS opens this link in a new tab. I would like to open it in a new UIWebView but the problem is that there's nothing differentiating an ad request from a pop up request in the UIWebView's delegate.
How is Safari doing it?
EDIT: The answer bellow is exactly the problem with UIWebView. It fools everyone into thinking that the delegate is the answer, when in fact you can do nothing without hacking your way through, even the simplest stuff.
EDIT 2: I have checked with other browsers as well like Mercury and they get this right. They understand that Twitter is a pop up and they open it in a new tab. So, this is not Apple's fault.
Set the delegate of your first web view to your view controller.
After that, add the following code to your view controller.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"%#", request.URL);
NSLog(#"%#", [request.URL scheme]);
if ([[request.URL scheme] isEqualToString:#"http"])
{
// Create web view here, because you're catching all requests
// Don't forget to set the delegate.
NSLog(#"Create new web view with %#", request.URL);
return NO;
}
return YES;
}
As you can see, you should replace the part where I added the NSLog. You're probably going to want to create a new web view at that point, and add it to the current view as a subview.
Why would you want to check on LinkClicked and not catch all requests?

UIWebView screenshot

I'm facing a problem trying to take screenshots form UIWebViews. I need to take some screenshots of my UIWebView and it works but the screenshot is not correct because they are taken in the event webViewDidFinishLoad, but it calls webViewDidFinishLoad when the UIWebView is not loaded fully, I mean, I take the screenshot in the event webViewDidFinishLoad but the UIWebView is not correctly and fully loaded so it takes a screenshot and it makes right but the screenshot is not totally correct because its the event is triggered (webViewDidFinishLoad) but the UIWebView is not totally loaded. Any ideas?
Thank you very much
A user was having a issue with their use of a activity indicator that apears until a page has loaded but theirs would come back after the main content had loaded the question is here UIWebView not finishing loading? and the answer code is used below. After the code I will go into detail about how this could be used for your situation.
(In the code below the web page truly starts loading on //show UIActivityIndicator and truly finishes loading the main content (not the extra content you are struggling with) on //hide UIActivityIndicator)
//Define the NSStrings "lastURL" & "currentURL" in the .h file.
//Define the int "falsepositive" in the .h file. (You could use booleans if you want)
//Define your UIWebView's delegate (either in the xib file or in your code)
- (void)webViewDidFinishLoad:(UIWebView *)webView {
lastURL = [[NSString alloc] initWithFormat:#"%#", webView.request.mainDocumentURL];
if (falsepositive != 1) {
NSLog(#"Loaded");
//hide UIActivityIndicator
} else {
NSLog(#"Extra content junk (i.e. advertisements) that the page loaded with javascript has finished loading");
//This method may be a good way to prevent ads from loading hehe, but we won't do that
}
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; {
NSURL *requestURL =[request mainDocumentURL];
currentURL = [[NSString alloc] initWithFormat:#"%#", requestURL]; //not sure if "%#" should be used for an NSURL but it worked...
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
if ([currentURL isEqualToString:lastURL]) {
falsepositive = 1;
NSLog(#"The page is loading extra content with javascript or something, ignore this");
} else {
falsepositive = 0;
NSLog(#"Loading");
//show UIActiviyIndicator
}
}
When a page loads in iOS, webviewdidstart and webviewdidfinish are called but not knowing the difference in your main page/html content objective-c calls this again for extra content such as ads or frames. Using this if I were in your situation I would create a BOOL such as pageIsLoading and set it to true in webviewdidstart and then set it to false in webviewdidfinish. After the BOOL is turned off in webviewdidfinish I would end webviewdidfinish by calling a method that will check after a short delay if the BOOL pageIsLoading == YES and if it is, do nothing because more content is loading. If pageIsloading == no then all content must be loaded and now would be a good time to take your snapshot.
Rather than taking screenshot in the event webViewDidFinishLoad you can try to take the screenshot explicitly using UIButton. Let this button be disabled and you can just set this button's enabled property to YES after web view fully loads its content.
On button click to can implement your code to take screenshot.
Edited : Webview's webViewDidFinishLoad method is called when it finishes loading its content. Might be its possible that it may be taking some time to render some images/content on its view.
If possible you can use NSTimer in your webViewDidFinishLoad method to wait for sufficient time(1 min) so that mean while webview can load its content fully.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60.0
target: self
selector: #selector(MethodName:)
userInfo: nil
repeats: NO];
In Selector method you can implement your code of taking screenshot.
Hope this will help you...

Objective-C calling javascript in UIWebView calling back Objective-C; threading issues

I have a UIWebView-based application that is storing some state. I have a native tab bar and upper bar however. What I need is that when I click on the native bar, I persist the data stored in the UIWebView.
To do this, I called evaluateJavaScriptByString to create a JSON object and it does a callback to objective-C via the UIWebViewDelegate protocol.
However, I find that the callback is asynchronous and hence my main transaction happens before the data is loaded.
Any idea how this problem can be solved?
Update: To explain the problem better:-
I have an HTML page with a form that someone puts some data into.
My Tab bar is native.
On the click of a button on the tab bar, I want the data from HTML saved to an Objective-C model, and the view should change to some other view.
What is happening is that once I click the button, the javascript call happens to the page (to create a JSON string to send to Objective-C) and this data does get saved to the obj-c model. However, this happens asynchronously. So my screen changes before the data is loaded into the model. If I refresh the next screen it shows the correct data. I was wondering if there was a way around this.
Note, I know how to call OBjective-C functions from WebViews. We are using JSOBjBridge for that anyway.
Just add something like this #"some_var = MAKE_JSON(); window.location = \"myapp://callback/\" + escape(some_var)" at the end of your javascript code, that you transfer to evaluateJavaScriptByString:
In UIWebView's delegate implement webView:shouldStartLoadWithRequest:navigationType: and catch all urls with myapp:// prefix like this:
- (BOOL) webView: (UIWebView *) webView
shouldStartLoadWithRequest: (NSURLRequest *) request
navigationType: (UIWebViewNavigationType) navigationType {
NSString *url = #"myapp://";
NSString *path = request.mainDocumentURL.relativePath;
NSString *callbackURL = #"callback/";
if ( [path hasPrefix:url] ) {
path = [path substringFromIndex:[url length]];
if ( [relPath hasPrefix:callbackURL] ) {
NSString *json = [path substringFromIndex:[callbackURL length]];
//TODO: Work with json
}
return NO;
}
return [super webView:webView shouldStartLoadWithRequest:request navigationType: navigationType];
}
I didn't test this code, I just written it from scratch, but it should work, I already did it in this way.

Resources