我的应用程序中有一个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];
}
}
当我单击自定义网址时,句柄错误()永远不会被调用,也不会决定PolicyForNavigationAction()。
好的,想通了...碰巧链接在新窗口中打开,因此添加下一个代码以及设置 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;
}
-(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,但可能会导致其他问题。感谢您的反馈!
iOS 13.1.3 上的 Swift 5.1(@hstdt 答案的变体),用于 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的答案获取和修改的。
Swift 5 的解决方案:以下是 mattblessed 对 WKWebView 的答案的变体,它适用于所有应用程序 URL 方案,而不仅仅是系统 URL-Schmes,如"sms:"、"tel:" 和 "mailto:"
此解决方案无需通过 LSApplicationQueriesSchemes
将自定义 URL 方案添加到您的应用程序 plist 文件中即可工作。(自iOS 10以来这是必需的 - 有关详细信息,请参阅本文:canOpenURL在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(":")
}
}
我认为这就是您想要的有关自定义方案;)
https://medium.com/glose-team/custom-scheme-handling-and-wkwebview-in-ios-11-72bc5113e344
可以使用setURLSchemeHandler:forURLScheme:
为给定的 URL 方案添加 URL 方案处理程序对象。
如果您想通过 Swift 添加自定义方案,请了解更多信息 http://samwize.com/2016/06/08/complete-guide-to-implementing-wkwebview/
我有用:
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)
}
}
}