When I click a pop up in wkWebView, I get that action via
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
if (!navigationAction.targetFrame.isMainFrame) {
//[webView loadRequest:navigationAction.request];
[self showInternalWeb:navigationAction.request.URL.absoluteString];
}
return nil;
}
and displaying the link in a different controller.
I would like to dismiss that controller automatically when the window is closed. In my laptop browser, the controller is automatically closed after I press a button on the new window. I am wondering how code this functionality myself.
I have tried calling:
[userContentController addScriptMessageHandler:self name:#"window.close()"];
[self.webView evaluateJavaScript:#"window.close()" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
NSLog(#"suces");
}];
This calls webViewDidClose normally, but not when the window becomes blank.
Please check that, did you set your UIDelegate for the webView..
webView.UIDelegate = self;
Related
I have a need to monitor all failed requests from a given web page loaded in WKWebKit. For this, I implemented a simple controller with WKWebView on it and also conformed that controller to WKNavigationDelegate:
- (void)viewDidLoad
{
[super viewDidLoad];
webView.navigationDelegate = self;
NSURL *url = [NSURL URLWithString: #"https://google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[webView loadRequest:request];
}
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
NSLog(#"webView.decidePolicyForNavigationResponse %#", navigationResponse.response);
decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSLog(#"webView.decidePolicyForNavigationAction %#", navigationAction.request);
decisionHandler(WKNavigationActionPolicyAllow);
}
I've also tried to implement custom NSURLProtocol extension but can see there just the initial request as with navigationDelegate:
[NSURLProtocol registerClass:[TrackingNSURLProtocol class]];
I can see my primary request to the google.com webpage in my output but the children's requests kicked off by that page are not tracked even though they are executed and downloaded by the same WKWebView when the page is loaded (css, js, images, etc).
Is it possible to achieve this kind of tracking with WKWebView (or in general with iOS)?
Depending on what "failed" means, you might prefer to implement the methods with fail in their names:
func webView(WKWebView, didFail: WKNavigation!, withError: Error)
Called when an error occurs during navigation.
func webView(WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error)
Called when an error occurs while the web view is loading content.
I have an app with UIWebView, which loads a web site https://app.bridallive.com/. Previously it was working well in iPad, but now it does not.
It loads neither in UIWebView (in a simplest possible app) and in iOS Safari. Works well in Mac OS.
I tried to diagnose it with:
-(void) loadWebSite {
NSURL * url = [NSURL URLWithString:#"https://app.bridallive.com/#/dashboard?iPadApp=true"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest: request];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadWebSite];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"Error : %#",error);
}
NSAppTransportSecurity is set to YES and the web site has valid and trusted certificate.
this is what I used in futher experiments:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadWebSite];
}
but this does not give any errors.
Can anyone suggest a way to diagnose this in iOS?
Best regards,
Andrey
UIWebview has a protocol, UIWebViewDelegate, it helps you to observe changes in your
It implements a few method using what you can observe the changes in your UIWebview loading lifecyclye:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"shouldStart");
return true;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
NSLog(#"webViewDidStartLoad");
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"webViewDidFinishLoad");
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"%#", error);
NSLog(#"didFailLoadWithError");
}
In order to receive the callback on this functions, sign up for the delegate on your classes interface in .h for example.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIWebViewDelegate>
#end
I have set up a sample project, and upon opening the URL, i receive the following error in the didFailLoadWithError callback:
Error Domain=WebKitErrorDomain Code=101 "(null)"
It seems you are not encoding you request properly. You should encode the string the following way:
NSString *encodedString=[[NSString stringWithFormat:#"https://app.bridallive.com/#/dashboard?iPadApp=true"] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
NSURL *weburl = [NSURL URLWithString:encodedString];
I am trying to build a filter for a UIWebView and I am struggiling to detect when the UIWebView has completely finished loading. I have used the following two methods
– webView:shouldStartLoadWithRequest:navigationType:
– webViewDidFinishLoad:
but the issue is that these will be called multiple times when a page has frames and additional content to load.
What I need is to know when the view has completely loaded and there is no more content to fetch. Then when the content has loaded I can check to URL of the page against a list of approved URLS.
ANy ideas?
Use the UIWebViewDelegate protocol method webViewDidFinishLoad and webView's isLoading property
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//Check here if still webview is loding the content
if (webView.isLoading)
return;
//after code when webview finishes
NSLog(#"Webview loding finished");
}
Swift 3 version:
func webViewDidFinishLoad(_ webView: UIWebView) {
if webView.isLoading{
return
}
print("Done loading")
}
Try use:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)requestURL navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [requestURL URL];
NSLog(#"##### url = %#",[url absoluteString]);
return YES;
}
don't forget to set your UIWebView Delegate
or add statement,
NSRange range = [[url absoluteString] rangeOfString:#"https://www.google.com"];
if (range.location != NSNotFound)
{}
hope to help you.
It is true that the original question was posted many years ago. Recently I had to find a reliable solution to this issue.
Here is the solution that worked for me:
Replaced UIWebView with WKWebWiew.
Added 'evaluateJavaScript' code while handling the 'didFinishNavigation' delegate method.
The complete code is:
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
[webView evaluateJavaScript:#"document.body.innerHTML" completionHandler:^(id result, NSError *error) {
if (result != nil) {
// Call your method here
}
if(error) {
NSLog(#"evaluateJavaScript error : %#", error.localizedDescription);
}
}];
}
If you aren't an owner of UIWebView and as a result you don't have an access to webViewDidFinishLoad but you have a reference to UIWebView you can use the next code
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
guard uiWebView.isLoading else {
return
}
timer.invalidate()
//Add your logic here
}
I have a code which places a phone call using below code :
// Make a call to given phone number
- (void)callPhoneNumber:(NSString *)phoneNumber
{
if (!self.webview)
self.webview = [[UIWebView alloc] init];
self.webview.delegate = self;
// Remove non-digits from phone number
phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:#""];
// Make a call
NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:#"tel:%#", phoneNumber]];
[self.webview loadRequest:[NSURLRequest requestWithURL:url]];
[self.view addSubview:self.webview];
}
This makes a call. What I want is, I want to know when user ends a call. I have to perform an operation when user ends a call. Is there any way for it?
What I tried is, I set delegate of webview to current controller. But none of the delegate methods is called.
- (void)webViewDidStartLoad:(UIWebView *)webView
{
DLog(#"Start Loading");
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
DLog(#"Finish Loading");
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
DLog(#"Did fail with error : %#", error);
}
I don't know if you need this info but I use webview so that when phone call is made, flow remains within the app and on call end, app screen is displayed rather than user manually coming to app from native contact app.
CoreTelephony framework has a CTCallCenter Class with callEventHandler property.
#property (nonatomic, copy) void (^callEventHandler)(CTCall*);
You will have to define a handler block in your application and assign it to this property.
If your application is active when a call event takes place, the system dispatches the event to your handler immediately when call state changes. Refer apple documents found here.
I used below code to get notified of call events.
// Create CTCallCenter object
callCenter = [[CTCallCenter alloc] init];
// Assign event handler. This will be called on each call event
self.callCenter.callEventHandler = ^(CTCall* call) {
// If call ended
if (call.callState == CTCallStateDisconnected)
{
NSLog(#"Call ended.");
}
};
For other call states check out Call States. For more info on CTCallCenter look at Apple doc for CTCallCenter.
You should be implementing this delegate method of UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
on end call operation your webview will notify this delegate method about your action perform and you can handle it in there for example
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if (navigationType == UIWebViewNavigationTypeLinkClicked )
{
NSURL *url = [request URL];
NSLog(#"URL ===== %#",url);
// you can check your end call operation URL and handle it accordingly
if ([[url absoluteString] rangeOfString:#"#"].location == NSNotFound)
{
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}
//return NO;
}
return YES;
}
Is it possible to download and add a passbook from within a webview without modifying the app to support the new MIME type or unknown MIME types like Sparrow did?
I have a news ios app with a webview. In the webview I display the news items and a banner. When you click the banner I want to open a url to a .pkpass file and add it to my passbook. Instead I get a FrameLoadInterrupted error and nothing visible happens.
If I open the url from safari this works fine, chrome, since earlier this week (version 23) also opens the url like intended.
Is this some weird strategy from Apple maybe? not allowing this MIME type to properly open from a webview?
My best bet is that the UIWebView is just not capable of handling the Passbook passes. You could however try and catch the downloading in the UIWebViewDelegate method -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType.
What I mean to say is that you have to handle this part your self, since the http://passkit.com/samples/ I used does not return an url which ends pkpass thus it is totally depended on how you request the passbook files.
If you do in include the .pkpass extension you can check for the extension in the request.
If you know what kind of URL the passbook file is at you write your own download code here and pass it to passbook via the passbook api.
There does not seem to be any great on fix for this, you could load the failed ULR in safari:
- (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"Webview: %#", error);
if ([error.domain isEqualToString:#"WebKitErrorDomain"] && error.code == 102) {
NSString *failedURL = [error.userInfo objectForKey:NSURLErrorFailingURLStringErrorKey];
if (failedURL == nil) {
return;
}
NSURL *url = [NSURL URLWithString:failedURL];
[[UIApplication sharedApplication] openURL:url];
}
}
But this is just really bad coding.
Okay, talked to the engineers at WWDC and this is a know bug in UIWebView but Apple probably won't fix it because they're encouraging people to adopt the new SFSafariViewController. We did come up with a hack to fix it should you need to support iOS 6-8:
Add the PassKit framework to the project if it isn't already.
#import <PassKit/PassKit.h>
Set up a delegate for the UIWebView (for example the view controller launching the UIWebView)
<UIWebViewDelegate>
Add a class variable to cache the UIWebView requests
NSURLRequest *_lastRequest;
Set the delegate
self.webView.delegate = self;
Add the callback to grab all requests and cache in case of failure
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
_lastRequest = request;
return YES;
}
Add the failure callback and re-fetch the URL to see if it is a pass and if so, present the pass to the user
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
// try to get headers in case of passbook pass
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:_lastRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// check for PKPass
if ([response.MIMEType isEqualToString:#"application/vnd.apple.pkpass"]) {
NSError *error;
PKPass *pass = [[PKPass alloc] initWithData:data error:&error];
if (error) {
NSLog(#"Error: %#", error);
} else {
PKAddPassesViewController *apvc = [[PKAddPassesViewController alloc] initWithPass:pass];
[self presentViewController:apvc animated:YES completion:nil];
}
}
}];
}
It's a horrible hack for what should be supported, but it works regardless of the extension and should support re-directs. If you want to pile on the bug train, you can reference radar://21314226