В моем приложении iOS у меня есть UIWebView.
Теперь я хочу, чтобы все ссылки, у которых атрибут target = "_ blank" не открывался внутри моего WebView, но внешне в Safari.
Как я могу это сделать?
В моем приложении iOS у меня есть UIWebView.
Теперь я хочу, чтобы все ссылки, у которых атрибут target = "_ blank" не открывался внутри моего WebView, но внешне в Safari.
Как я могу это сделать?
Мой ответ, который является ответом, который я обнаружил при переполнении стека для Android 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];
}
Это разрешает проблему "target =" _ blank "для открытия в сафари, и продолжает открывать стандартные ссылки в веб-просмотре.
Проблема с решением wedo заключается в том, что все ваши ссылки откроются в Safari.
Два решения:
1 - обратный вызов JavaScript на Objective-C, когда target = "_ blank"
Чтобы решить проблему, вам нужно добавить javascript на все ваши ссылки, проверить, есть ли у них атрибут _blank, затем перезвонить код Objective-C с JavaScript и запустить:
[[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...)
Престижность Мартину Магакяну! Вот модификация, основанная на предположении 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;
}
Я основал свой ответ на одном из Benjamin Piette, но мне нужно было настроить script, поскольку ссылки, которые нужно отрегулировать в моем случае, были сгенерированы асинхронно другим 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
Я использовал этот способ в своем проекте, где frameset используется в корневом HTML, загруженном в WebView. Все перекрестные ссылки, указывающие на другой существующий кадр, не вызывают второго вызова сообщения, поэтому здесь обрабатываются только новые (внешние) цели. Он работает нормально для меня.
На всякий случай, если кто-то ищет ответ в Swift4
Для внутренних загрузок убедитесь, что вы вызываете решение figureHandler() с помощью .cancel, чтобы загрузка прекратилась, а также вызывало 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)
}