WKWebView无法打开自定义URL scheme(JavaScript在新窗口中打开自定义scheme链接)

9

我的应用程序中有一个WKWebView。我不使用UIWebView,因为由于某些奇怪的原因它不能正确地打开包含大量JS代码的网页。

当我点击具有自定义URL方案“scm://”的链接时,它什么都没有...

我的代码:

- (void)viewDidLoad {
    // ...

    WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
    if ([configuration respondsToSelector:@selector(setDataDetectorTypes:)])
        [configuration setDataDetectorTypes:WKDataDetectorTypeLink];

    myWebView = [[WKWebView alloc] initWithFrame:webFrame configuration:configuration];
    myWebView.navigationDelegate = self;

    [self.view addSubview:myWebView];
}

#pragma mark - WKNavigationDelegate

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSURL *requestURL = navigationAction.request.URL;
    UIApplication *app = [UIApplication sharedApplication];
    if ([requestURL.scheme.lowercaseString isEqualToString:@"scm"] && [app canOpenURL:requestURL]) {
        [app openURL:requestURL];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else
        decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    [self handleError:error];
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    [self handleError:error];
}

#pragma mark - Handle web view errors.

- (void)handleError:(NSError *)error
{
    UIApplication *app = [UIApplication sharedApplication];
    app.networkActivityIndicatorVisible = NO;

    NSURL *failedUrl = error.userInfo[NSURLErrorFailingURLErrorKey];

    if ([failedUrl.scheme.lowercaseString isEqualToString:@"scm"]) {
        [app openURL:failedUrl];
    }
}

当我点击自定义URL时,handleError()和decidePolicyForNavigationAction()都没有被调用。
6个回答

11

好的,我弄清楚了...... 问题在于链接正在新窗口中打开,因此除了设置UIDelegate之外,添加下一个代码使其起作用。

    // Somewhere in viewDidLoad()...
    myWebView.UIDelegate = self;
}

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
    NSLog(@"createWebViewWithConfiguration %@ %@", navigationAction, windowFeatures);
    if (!navigationAction.targetFrame.isMainFrame) {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        [(WKWebView *)_webView loadRequest:navigationAction.request];
    }
    return nil;
}

尝试设置UIDelegate但没有成功,看起来你之前做错了什么 ;) - trungduc
是的,我没有添加_createWebViewWithConfiguration_方法,因为不幸的是教程_完整实现WKWebView指南_没有提到它。 - Borzh

8
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

    NSURLRequest *request = navigationAction.request;
    if(![request.URL.absoluteString hasPrefix:@"http://"] && ![request.URL.absoluteString hasPrefix:@"https://"]) {
        if([[UIApplication sharedApplication] canOpenURL:request.URL]) {
            //urlscheme, tel, mailto, etc.
            [[UIApplication sharedApplication] openURL:request.URL];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

注意:本答案仅关注urlscheme,但可能会导致其他问题。感谢您的反馈!


8
以下代码适用于iOS 13.1.3上的Swift 5.1(变体版@hstdt's answer),最小化处理WKWebView以测试以下模式:sms:tel:mailto:。将以下内容添加到设置WKWebView的任何位置。
// assign the delegate
webView.navigationDelegate = self

然后,在您的类中的任意位置添加以下函数。
func webView(_ webView: WKWebView,
             decidePolicyFor navigationAction: WKNavigationAction,
             decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

    // if the request is a non-http(s) schema, then have the UIApplication handle
    // opening the request
    if let url = navigationAction.request.url,
        !url.absoluteString.hasPrefix("http://"),
        !url.absoluteString.hasPrefix("https://"),
        UIApplication.shared.canOpenURL(url) {

        // have UIApplication handle the url (sms:, tel:, mailto:, ...)
        UIApplication.shared.open(url, options: [:], completionHandler: nil)

        // cancel the request (handled by UIApplication)
        decisionHandler(.cancel)
    }
    else {
        // allow the request
        decisionHandler(.allow)
    }
}

解释:

  • WKWebView 似乎无法处理非 http(s) 的 url 模式。
  • 上述代码会在 WKWebView 渲染/加载请求之前捕获并检查不同的模式。
  • 我们检查非 http(s) 模式,并让 UIApplication 处理它。

注意:如果您发现其他模式使用此解决方案是否有效,请评论说明。

再次强调,此解决方案源自 @hstdt's answer 并进行了修改。


无法使用 intent://,错误信息为:“此应用程序不允许查询意图方案”。 - Md Imran Choudhury
@MdImranChoudhury 什么应用程序/服务使用 intent:// uri? - mattblessed

5

Swift 5解决方案: 以下是mattblessed答案的变化版本,适用于所有应用程序URL-Scheme,而不仅仅是系统URL-Scheme,如“sms:”、“tel:”和“mailto:”

此解决方案将在不通过LSApplicationQueriesSchemes将自定义URL Schemes添加到应用程序plist文件的情况下工作。 (这是必要的自iOS 10以来-有关详细信息,请参见本文:canOpenURL not working in ios 10

将以下WKNavigationDelegate实现添加到您的类中:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if let requestUrl = navigationAction.request.url, requestUrl.isCustomUrlScheme() {
        decisionHandler(.cancel)

        // try to open URLs like e.g. "whatsapp://"
        UIApplication.shared.open(requestUrl, options: [:]) { success in
            if !success {
                //TODO 
                // add your code for handling URLs that can't be opened here - 
                // maybe show an error alert
            }
        }
    } else {
        // allow the request
        decisionHandler(.allow)
    }
}

将以下URL扩展名添加到您的类中:

extension URL {

    func isCustomUrlScheme() -> Bool {
        let webUrlPrefixes = ["http://", "https://", "about:"]

        let urlStringLowerCase = self.absoluteString.lowercased()
        for webUrlPrefix in webUrlPrefixes {
            if urlStringLowerCase.hasPrefix(webUrlPrefix) {
            return false
        }

        return urlStringLowerCase.contains(":")
    }
}

2

1
这仅适用于iOS 11及以上版本... 我该如何在早期的iOS版本上修复它? - Borzh
如果你能看懂一点Swift代码,可以看看这个链接。这是一个“实现WKWebView的完整指南”http://samwize.com/2016/06/08/complete-guide-to-implementing-wkwebview/。如果不行,请告诉我 ;) - trungduc
是的,我明白了。看起来你忘记添加 webView.UIDelegate = self 了。是这样吗? - trungduc
尝试设置UIDelegate但没有成功。 - Borzh
你修好了吗?如果没有,你能创建一个 Github 项目并给我链接吗?我想尝试调试它。 - trungduc
显示剩余2条评论

0
这个对我有用:

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
    handleError(error: error as NSError)
}

func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
    handleError(error: error as NSError)
}

func handleError(error: NSError) {
    if let failingUrl = error.userInfo[NSURLErrorFailingURLStringErrorKey] as? String {
        if let url = NSURL(string: failingUrl) {
            UIApplication.shared.openURL(url as URL)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接