ios 在 Safari 中打开 UIWebView 之外的 target="_blank" 链接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8490038/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Open target="_blank" links outside of UIWebView in Safari
提问by Norwald2
Inside my iOS Application I have an UIWebView.
在我的 iOS 应用程序中,我有一个 UIWebView。
Now I want all links that have the attribute target="_blank" not to open inside my WebView but externally in Safari.
现在我希望所有具有属性 target="_blank" 的链接不要在我的 WebView 内部打开,而是在 Safari 外部打开。
How can I do this?
我怎样才能做到这一点?
回答by Benjamin Piette
My answer, which is a from an answer I found on stack overflow for the Android WebView. But actually, both webview have the same problem and same (dirty) fix:
我的答案来自我在 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];
}
This solves both the target="_blank" issue to open in safari, AND keeps opening standard links within the webview.
这既解决了在 safari 中打开的 target="_blank" 问题,也解决了在 webview 中打开标准链接的问题。
回答by Martin Magakian
The problem with wedo's solution is that all your linkswill open in Safari.
wedo 解决方案的问题是您的所有链接都将在 Safari 中打开。
Two solutions:
两种解决方案:
1 - JavaScript callback to Objective-C when target="_blank"
To achieve your problem you need to add some javascript on all your links, check if they have the attribute _blank, then call back your objective-C code from JavaScript and run:
1 - 当 target="_blank" 时 JavaScript 回调到 Objective-C
要解决您的问题,您需要在所有链接上添加一些 javascript,检查它们是否具有属性 _blank,然后从 JavaScript 回调您的 Objective-C 代码并运行:
[[UIApplication sharedApplication] openURL:myUrl];
I personally don't like this solution because it's a lot of code, callback, complexity and a bit tricky...
我个人不喜欢这个解决方案,因为它有很多代码、回调、复杂性和有点棘手......
2 - Checking url parameter
If you have access to the HTML code (note in both solution you need access to HTML) I recommend you remove the target="_blank" and add the parameter ?openInSafari=true
2 - 检查 url 参数
如果您有权访问 HTML 代码(请注意,在这两种解决方案中您都需要访问 HTML),我建议您删除 target="_blank" 并添加参数 ?openInSafari=true
In the UIWebViewDelegate add the following code:
在 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;
}
A nice (bad?) point with this solution is that if the link x levels deeper can still open links into safari browser
这个解决方案的一个好(坏?)点是,如果链接 x 级别更深仍然可以打开链接到 safari 浏览器
<a href="http://www.google.com?openInSafari=true">Google in Safari</a>
Always add the protocol in the URL (http, https...)
始终在 URL 中添加协议(http、https...)
回答by mpemburn
Kudos to Martin Magakian! Here is the modification based on spankmaster79's suggestion:
向马丁·马加基安致敬!这是基于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;
}
回答by Sujay Patil
Just in case if some one is looking for answer in Swift4
以防万一有人在 Swift4 中寻找答案
For internal loads make sure you call the decisionHandler() closure with .cancel so the load halts, while also calling UIApplication.shared.open() to have the URL open in the external browser.
对于内部加载,请确保使用 .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)
}
回答by Sujith Sreedhar
Try this.
尝试这个。
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);
回答by clauswey
I based my answer on the one from Benjamin Piette but needed to adjust the script since the links to be adjusted in my case were generated asynchronously by an other javascript.
我的回答基于 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];
}
回答by DeadlineX
I had the same question and unfortunately these answers drew me in a completely wrong and very complex way. Really the question is answered as simply as "you need to use WebViewPolicyDelegateProtocol".
我有同样的问题,不幸的是,这些答案以一种完全错误且非常复杂的方式吸引了我。实际上,这个问题的回答就像“您需要使用 WebViewPolicyDelegateProtocol”一样简单。
In -viewDidLoad of the view controller implementation you write:
在您编写的视图控制器实现的 -viewDidLoad 中:
[myWebView setPolicyDelegate:self];
In your view controller class interface you must add two items:
在您的视图控制器类接口中,您必须添加两项:
- (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;
And implement them as easy as:
并像实现它们一样简单:
- (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];
}
}
Also refer to the Apple docs
另请参阅Apple 文档
I've used this way in my project, where frameset is used in the root HTML, loaded into the WebView. All cross-links pointing to another existing frame don't cause the second message call, so only new (external) targets are processed here. It works OK for me.
我在我的项目中使用了这种方式,其中框架集在根 HTML 中使用,加载到 WebView 中。指向另一个现有框架的所有交叉链接不会导致第二个消息调用,因此这里只处理新的(外部)目标。它对我有用。