【发布时间】:2012-01-19 09:00:02
【问题描述】:
在我的 iOS 应用程序中,我有一个 UIWebView。
现在我希望所有具有属性 target="_blank" 的链接不要在我的 WebView 内部打开,而是在 Safari 外部打开。
我该怎么做?
【问题讨论】:
在我的 iOS 应用程序中,我有一个 UIWebView。
现在我希望所有具有属性 target="_blank" 的链接不要在我的 WebView 内部打开,而是在 Safari 外部打开。
我该怎么做?
【问题讨论】:
我的答案来自我在 Android WebView 的堆栈溢出中找到的答案。但实际上,两个 webview 都有相同的问题和相同的(脏)修复:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([request.URL.absoluteString hasPrefix:@"newtab:"])
{
// JS-hacked URl is a target=_blank url - manually open the browser.
NSURL *url = [NSURL URLWithString:[request.URL.absoluteString substringFromIndex:7]];
[[UIApplication sharedApplication] openURL:url];
return true;
}
return true;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// JS Injection hack to solve the target="_blank" issue and open a real browser in such case.
NSString *JSInjection = @"javascript: var allLinks = document.getElementsByTagName('a'); if (allLinks) {var i;for (i=0; i<allLinks.length; i++) {var link = allLinks[i];var target = link.getAttribute('target'); if (target && target == '_blank') {link.setAttribute('target','_self');link.href = 'newtab:'+link.href;}}}";
[webView stringByEvaluatingJavaScriptFromString:JSInjection];
}
这解决了在 safari 中打开 target="_blank" 的问题,并在 webview 中保持打开标准链接。
【讨论】:
WebChromeClient.onCreateWindow 方法在技术上拦截<a target="_blank" ... 链接甚至javascript window.open(....。虽然你有一个不同的问题,因为 URL 没有在方法中给出,并且必须做其他骇人听闻的事情。
native:// 和命令,必须在 javascript 中转义 URL,然后取消转义它回到webView:shouldStartLoadWithRequest:...,但这个简单的前置要干净得多。另外,我需要在一个不能依赖 jQuery 的新项目上执行此操作,因此您的基本 javascript 非常有用。谢谢!
wedo 解决方案的问题是所有链接都会在 Safari 中打开。
两种解决方案:
1 - 当 target="_blank" 时 JavaScript 回调到 Objective-C
要解决您的问题,您需要在所有链接上添加一些 javascript,检查它们是否具有属性 _blank,然后从 JavaScript 回调您的 Objective-C 代码并运行:
[[UIApplication sharedApplication] openURL:myUrl];
我个人不喜欢这个解决方案,因为它有很多代码、回调、复杂并且有点棘手......
2 - 检查 url 参数
如果您可以访问 HTML 代码(请注意在这两种解决方案中您都需要访问 HTML)我建议您删除 target="_blank" 并添加参数 ?openInSafari=true
在 UIWebViewDelegate 中添加如下代码:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
NSURL *url = [request URL];
NSDictionary *param = [url queryParameters];
NSString *openIsSafari = [param objectForKey:@"openInSafari"];
if ( openIsSafari!= nil && ([openIsSafari isEqualToString:@"true"] || [openIsSafari isEqualToString:@"1"])){
[[UIApplication sharedApplication] openURL:url];
return NO;
}
}
return YES;
}
这个解决方案的一个好(坏?)点是,如果链接 x 级别更深,仍然可以打开到 safari 浏览器的链接
<a href="http://www.google.com?openInSafari=true">Google in Safari</a>
始终在 URL 中添加协议(http、https...)
【讨论】:
queryParameters 不是 NSURL 的可用函数。您需要解析来自[url query]的参数
向 Martin Magakian 致敬!以下是根据 spankmaster79 的建议进行的修改:
- (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest: (NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
NSURL *url = [request URL];
NSString *param = [url query];
if ([param rangeOfString: @"openInSafari=true"].location != NSNotFound){
[[UIApplication sharedApplication] openURL: url];
return NO;
}
}
return YES;
}
【讨论】:
以防万一有人在 Swift4 中寻找答案
对于内部加载,请确保使用 .cancel 调用 decisionHandler() 闭包,以便加载停止,同时调用 UIApplication.shared.open() 以在外部浏览器中打开 URL。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
if url.host == "your url.com" {
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
}
}
decisionHandler(.allow)
}
【讨论】:
试试这个。
UIApplication *app = [UIApplication sharedApplication];
NSURL *url = navigationAction.request.URL;
if (!navigationAction.targetFrame) {
if ([app canOpenURL:url]) {
[app openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
if ([url.scheme isEqualToString:@"mailto"])
{
if ([app canOpenURL:url])
{
[app openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
decisionHandler(WKNavigationActionPolicyAllow);
【讨论】:
我的回答基于 Benjamin Piette 的回答,但需要调整脚本,因为在我的案例中要调整的链接是由其他 javascript 异步生成的。
NSString* const kOpenInNewTabPrefix = @"myOpenInNewTabPrefix:";//This NEEDS to end with ':'
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([[request.URL.absoluteString lowercaseString] hasPrefix:[kOpenInNewTabPrefix lowercaseString]])
{
// JS-hacked URl is a target=_blank url - manually open the browser.
NSURL *url = [NSURL URLWithString:[request.URL.absoluteString substringFromIndex:[kOpenInNewTabPrefix length]]];
[[UIApplication sharedApplication] openURL:url];
return YES;
}
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//based on http://stackoverflow.com/questions/8490038/open-target-blank-links-outside-of-uiwebview-in-safari
// JS Injection hack to solve the target="_blank" issue and open a real browser in such case.
NSString *JSInjection = [NSString stringWithFormat:@"javascript: "
"document.getElementsByTagName('body')[0].addEventListener('click', function(e){"
" var a = e.target;"
" if(a.nodeName != 'A'){"
" return;"
" }"
" var target = a.target;"
" var href = a.href;"
" var prefix = '%@';"
" if(href.substring(0, %lu) != '%@' && target == '_blank'){"
" a.href = prefix + href;"
" }"
"})"
, [kOpenInNewTabPrefix lowercaseString]
, (unsigned long)[kOpenInNewTabPrefix length]
, [kOpenInNewTabPrefix lowercaseString]];
[webView stringByEvaluatingJavaScriptFromString:JSInjection];
}
【讨论】:
我有同样的问题,不幸的是,这些答案以一种完全错误且非常复杂的方式吸引了我。确实,问题的答案很简单,就像“您需要使用 WebViewPolicyDelegateProtocol”。
在您编写的视图控制器实现的 -viewDidLoad 中:
[myWebView setPolicyDelegate:self];
在您的视图控制器类接口中,您必须添加两项:
- (void)webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id<WebPolicyDecisionListener>)listener;
- (void)webView:(WebView *)webView
decidePolicyForNewWindowAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
newFrameName:(NSString *)frameName
decisionListener:(id<WebPolicyDecisionListener>)listener;
并像这样简单地实现它们:
- (void)webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id<WebPolicyDecisionListener>)listener {
// just the default behavior, though you're free to add any url filtering you like...
[listener use];
}
- (void)webView:(WebView *)webView
decidePolicyForNewWindowAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
newFrameName:(NSString *)frameName
decisionListener:(id<WebPolicyDecisionListener>)listener {
// frameName is your "target" parameter value
if([frameName isEqualToString:@"_blank"]) {
[[NSWorkSpace sharedWorkSpace] loadURL:[request URL]];
} else {
[listener use];
}
}
另请参阅Apple docs
我在我的项目中使用了这种方式,其中框架集用于根 HTML 中,加载到 WebView 中。所有指向另一个现有框架的交叉链接都不会导致第二个消息调用,因此这里只处理新的(外部)目标。它对我来说没问题。
【讨论】:
UIWebView (iOS),而不是 macOS。