How To Clear A UIWebView - ios

I am very new to the whole programming business, and was wondering if there is any way to clear the contents of a UIWebView in iphone programming, so that the loading symbol for the next view is not showing up in front of the last view.
Many Thanks,
Thomas

Try setting the URL to about:blank and reload the page.

Just load an empty html string into it
[self.webView loadHTMLString:#"" baseURL:nil];

Answer extension for documentation purposes to maybe help someone else:
I had the same desire (clear content before loading next url) but had a UIWebView delegate set to receive webviewDidFinishLoad:(UIWebView)webview message and update another part of UI in response.
Problem: the call to clear the content to also called delegate method, so getting false-hits (that is, getting call when clear is done, too, but delegate is coded to expect call only when real content is loaded).
Solution: use a known URL for clear, and have webviewDidFinishLoad: ignore calls made when that URL is finished:
- (void) startLoadOfNextURL:(NSURL*)url
{
// clear:
[self.webView loadHTMLString:#"" baseURL:nil];
// Load real next URL
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(#"WebView finished loading: %#", webView);
if ([self.webView.request.URL.absoluteString isEqualToString:#"about:blank"]) {
NSLog(#" This is Blank. Ignoring as false event.");
}
else {
NSLog(#" This is a real url");
[self updateUIInSomeWay];
}
}
Note: using this:
[self.webView loadHTMLString:#"about:blank" baseURL:nil];
actually causes the words "about:blank" to appear as text in the webview's content pane!
Final complication: In practice, with my two [webview load...] calls so close together, I was finding that instead of a "loaded" event for the clear, the webview was actually canceling it in favor of the second request and calling webView: didFailLoadWithError: for the first load request. Thus, I had to put similar code in that event:
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(#"WebView error on: %#", webView);
NSLog(#"Error is: %#", error);
NSURL* failingURL = [error.userInfo objectForKey:#"NSErrorFailingURLKey"];
if ([failingURL.absoluteString isEqualToString:#"about:blank"]) {
NSLog(#" This is Blank. Ignoring.");
}
else {
NSLog(#" This is a real URL.");
[self doSomethingAboutError];
}
}

Swift, Xcode 7 beta 5
webView.loadRequest(NSURLRequest(URL: NSURL(string: "about:blank")!))

Swift 4.0 , XCODE 9
webView.loadRequest(URLRequest.init(url: URL.init(string: "about:blank")!))

Same answer in Swift 4.2, xCode 10
if let clearURL = URL(string: "about:blank") {
myWebView.loadRequest(URLRequest(url: clearURL))
}

Related

How to manipulate iFrames in a WKWebView?

I want to inject custom headers into NSURLRequests made for top-level frames of WKWebView and all of its iFrames.
To achieve this I am listening to the
- webView:decidePolicyForNavigationAction:decisionHandler: method. In this method i want to cancel the existing page load and create a new NSURLRequest and reload the page.
Here's the code snippet I am using,
- (void)webView:(WKWebView*)webView
decidePolicyForNavigationAction:(WKNavigationAction*)action
decisionHandler:
(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString* header = #"Custom-Header";
if([action.request.allHTTPHeaderFields objectForKey:header] ){
NSLog(#"Header already added for URL - %#",action.request.URL.absoluteString );
}
else{
decisionHandler(WKNavigationActionPolicyCancel);
NSLog(#"Adding header for URL %#",action.request.URL.absoluteString);
NSMutableURLRequest *mutableRequest = [action.request mutableCopy];
NSString* headerVal;
if(action.targetFrame.mainFrame)
{
headerVal = #"MAINFRAME";
}
else
{
headerVal = #"SUBFRAME";
}
[mutableRequest addValue:headerVal forHTTPHeaderField:header];
[action.targetFrame.webView loadRequest:mutableRequest];
return;
}
// further processing
}
However with this approach I face the following problem - If the callback is received for a iFrame, the entire page reloads with the iFrame URL. Instead I want only the iFrame to reload with the URL. Is there any way to solve this?
Note that at the end of this I want to be able to send a custom header indicating whether the request originated from the main frame or an iFrame.Please do suggest if there are better solutions to achieve this.

WKWebView Hangs on local webpage load with certain web configuration.

I have a WKWebView that loads a local set of webpages using WKWebViewConfiguration to set the configuration for #"allowFileAccessFromFileURLs" to be true.
The request is set up with something like this:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"testPage" withExtension:#"html" subdirectory:#"html/pages"];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0f];
Then the request is sent using the following WKWebview method:
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
my problem is where the device has a current connection but there is no network traffic. The webview as an element on the screen will be added to the screen and the request will be made but the webview will show a white screen for about 50 seconds before displaying the local content.
Everything in the webview loads regardless of the network status as its loaded locally when there is no link conditioner set.
For example if the device is connected to wireless but the network link conditioner is set to 100% loss. The webview is created and the request is sent to load the local content triggering the hang of the load.
I had a thought that it might be the WKWebView trying to do some kind of validation in the background that requires a network transaction but I did some network profiling with instruments and also some timeline recording in the safari webview and I couldnt see anything that would cause it to hang.
The only reason I can think of it loading local content after 50 seconds or so is that its hit some sort of WKWebView timeout to load a network connection.
Any help would be greatly appreciated, thanks.
Okay so for anyone else who stumbles across this I have found what I was doing wrong.
The issue was not actually the WKWebview or the web content itself it was how I was handling the completion of the webview loading.
in the method:
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
I was listening for a completion of events by evaluating some JS like so:
-(void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
[webView evaluateJavaScript:#"document.body.innerHTML" completionHandler:^(id result, NSError *error)
{
if (result != nil) {
[self doCertainNetworkEvent]; //here another method is called with a networking function inside of it.
}
if(error)
{
NSLog(#"evaluateJavaScript error : %#", error.localizedDescription);
}
}];
}
The completion block of course couldnt finish until the network function within the didFinishNavigation method call was finished (which it couldnt because there was no traffic.)

how can I clear the contents of a UIWebView/WKWebView?

I have a webview that can be cycled through different URLs. When I switch from one to the other I want the old web page to disappear before loading the next one. How can I do this without re allocating the webview?
If I try to [self.webView loadHTMLString:#"" baseURL:nil]; and load my URL in the same function the previous web page still remains:
[self.webView loadHTMLString:#"" baseURL:nil];
[self.webView loadRequest:[NSURLRequest requestWithURL:self.pageURL]];
EDIT: this apparently isn't clear enough for you. the below code doesn't clear the webview, it displays the previous page until the new one is loaded:
- (void) startLoadOfNextURL:(NSURL*)url
{
// clear:
[self.webView loadHTMLString:#"" baseURL:nil]; //DOESNT WORK
// Load real next URL
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
You can write the code below while your controller dismisses.
webView.load(URLRequest(url: URL(string:"about:blank")!))
You can make it load a blank page
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
Use JavaScript instead.
webView.evaluateJavaScript("document.body.remove()")
The following code clears the screen and then navigates
Swift
webView.evaluateJavaScript("document.documentElement.remove()") { (_, _) in
self.webView.load(urlRequest)
}
To clear old contents of webview
When you call - loadHTMLString:baseURL: it doesn't block until the load is complete. Once it records your request, it returns and loads in the background.
As a result, you would need to wait for the first load to finish before kicking off a new load request.
With UIWebView you would use UIWebViewDelegate's
- webViewDidFinishLoad:.
With WKWebView you would use WKNavigationDelegate's
- webView:didFinishNavigation:
Another approach if you really wanted to clear the contents without a delegate method would be to use JavaScript (eg https://stackoverflow.com/a/4241420/3352624). Then for UIWebView you could invoke - stringByEvaluatingJavaScriptFromString:. That method will block execution until the JavaScript executes and returns.
For WKWebView, you would need to do something like https://stackoverflow.com/a/30894786/3352624 since its - evaluateJavaScript:completionHandler: doesn't block.
To make old contents "disappear"
If you really just want to make "the old web page to disappear", you could cover the content area of the webview with a blank UIView temporarily. You could hide the contents when you initiate the load and then show the contents using the delegate methods above after the load completes.
Swift
webView.load(URLRequest(url: URL(string: "about:blank")!))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
//Load request or write code here
})

how do i call objective c code from javascript

i've some javascript code on the webpage in the uiwebview that i want to use to call one of my objective c methods.
i found some code online which i decided to use. but it still doesn't seem to be working. can anyone see where the problem is?
javascript code:
function someMethod() {
window.location = "ios:webToNativeCall";
}
objective c code:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] absoluteString] hasPrefix:#"ios:"]) {
// Call the given selector
[self performSelector:#selector(webToNativeCall)];
// Cancel the location change
return NO;
}
return YES;
}
-(void)webToNativeCall
{
//my code here
}
i'm not sure, how to use this method so it might be that i have implemented it incorrectly.
does anyone have any ideas about what could be causing this?
Thanks in advanced.
This code looks ok, please check whether delegate for UIWebView is set or not.
Otherwise you can use EasyJSWebView download it from Github, it is easy to use.
You must have missed to link the delegate.
Either connect the delegate of the webView to the file owner in the .xib file
or
Use Following code
webView = [[UIWebView alloc] init];
webView.delegate = self;
in your viewDidLoad also write below code
[webView stringByEvaluatingJavaScriptFromString:#"yourJavascriptFunction()"];
Hope it helps you...

webViewDidFinishLoad called multiple times for single loadrequest

I have been using webView delegate successfully from long time. But recently I faced strange issue with this delegate. In my current project I am trying to access my router from webview. I am passing username and password inside URL only. Below is load request code.
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://uname:password#192.168.1.1"]]];
This calls webView delegate method (webViewDidFinishLoad and webViewDidStartLoad) 5 times. Is it expected? When I pass simple URL like google.com it works as expected. But with username and password why these delegate methods are called 5 times?
If this behaviour is correct then I need to know why it calls 5 times only. The reason is, in my program - I am calling performSegueWithIdentifier in webViewDidFinishLoad method and in present form it calls segue 5 times. For workaround I can maintain count and will call performSegueWithIdentifier on 5th count only.
Thanks
webViewDidStartLoad/webViewDidFinishLoad are called once per HTML frame. Your content likely has multiple frames in it.
See UIWebViewDelegate docs.
webViewDidStartLoad:
Sent after a web view starts loading a frame.
This Methods works for me... :)
#pragma mark UI Web View Delegate
NSInteger webViewLoads;
//a web view starts loading
- (void)webViewDidStartLoad:(UIWebView *)webView{
webViewLoads++;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[SVProgressHUD showWithStatus:#"Loading..." maskType:SVProgressHUDMaskTypeBlack];
}
//web view finishes loading
- (void)webViewDidFinishLoad:(UIWebView *)webView{
webViewLoads--;
[self performSelector:#selector(webViewFinishLoadWithCondition) withObject:nil afterDelay:0.5];
}
//web view handling error
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
webViewLoads--;
NSLog(#"Web View Did Fail Load With Error : %#",error);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[SVProgressHUD dismiss];
}
-(void)webViewFinishLoadWithCondition{
if(webViewLoads==0){
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[SVProgressHUD dismiss];
}
}
As webViewDidStartLoad/webViewDidFinishLoad are called once per HTML frame, Use an integer to find when the last frame load is finished,
In detail :
Increment the integer in webViewDidStartLoad
Decrement the integer in webViewDidFinishLoad
In webViewDidFinishLoad check when integer is zero, Which means all the frames of web page are loaded, Now call the selector
This is an explanation of #marvin's answer
Best approach:
Check for isLoading in webViewDidFinishLoad and isLoading is false, do what ever u want
-(void)webViewDidFinishLoad:(UIWebView *)webview
{
if (!webview.isLoading)
{
// webview is done loading content.
// `performSegueWithIdentifier` / Your code Here
}
}

Resources