In meiner iOS-Anwendung habe ich eine UIWebView.
Nun möchte ich, dass alle Links, die das Attribut target = "_ blank" haben, nicht in meinem WebView, sondern extern in Safari geöffnet werden.
Wie kann ich das machen?
Meine Antwort, eine Antwort, die ich im Stack-Überlauf für Android WebView gefunden habe. Aber eigentlich haben beide Webview das gleiche Problem und die gleiche (schmutzige) Korrektur:
- (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];
}
Dadurch wird sowohl das Problem target = "_ blank" gelöst, als auch in Safari geöffnet, UND es werden weiterhin Standardlinks in der Webansicht geöffnet.
Das Problem mit der Lösung von wedo ist, dass alle Ihre Links in Safari geöffnet werden.
Zwei Lösungen:
1 - JavaScript-Rückruf an Objective-C, wenn target = "_ blank"
Um Ihr Problem zu lösen, müssen Sie Javascript für alle Ihre Links hinzufügen. Überprüfen Sie, ob sie das Attribut _blank haben. Rufen Sie dann Ihren Objective-C-Code aus JavaScript ab und führen Sie Folgendes aus:
[[UIApplication sharedApplication] openURL:myUrl];
Ich persönlich mag diese Lösung nicht, weil sie viel Code, Callback, Komplexität und ein bisschen knifflig ist ...
2 - URL-Parameter überprüfen
Wenn Sie Zugriff auf den HTML-Code haben (beachten Sie, dass Sie in beiden Lösungen Zugriff auf HTML benötigen), empfehle ich, dass Sie das target = "_ blank" entfernen und den Parameter? OpenInSafari = true hinzufügen
Fügen Sie im UIWebViewDelegate den folgenden Code hinzu:
-(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;
}
Ein netter (schlechter?) Punkt bei dieser Lösung ist, dass bei tieferen Linkebenen noch Links in den Safari-Browser geöffnet werden können
<a href="http://www.google.com?openInSafari=true">Google in Safari</a>
Fügen Sie das Protokoll immer in die URL ein (http, https ...)
Kudos an Martin Magakian! Hier ist die Modifikation basierend auf dem Vorschlag von 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;
}
Nur für den Fall, dass jemand in Swift4 nach einer Antwort sucht
Stellen Sie bei internen Ladevorgängen sicher, dass Sie den Closure-Befehl decisionHandler () mit .cancel aufrufen, damit der Ladevorgang angehalten wird, und rufen Sie UIApplication.shared.open () auf, um die URL im externen Browser zu öffnen.
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)
}
Ich hatte die gleiche Frage und leider haben mich diese Antworten völlig falsch und sehr komplex gezeichnet. Die Frage wird wirklich so einfach wie "Sie müssen WebViewPolicyDelegateProtocol verwenden" beantwortet.
In -viewDidLoad der View Controller-Implementierung, die Sie schreiben:
[myWebView setPolicyDelegate:self];
In Ihrer View Controller-Klassenschnittstelle müssen Sie zwei Elemente hinzufügen:
- (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;
Und implementiere sie so einfach wie:
- (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];
}
}
Siehe auch die Apple-Dokumente
Ich habe diese Methode in meinem Projekt verwendet, bei dem Frameset im Stamm-HTML verwendet und in die WebView geladen wird. Alle Querverweise, die auf einen anderen vorhandenen Frame verweisen, verursachen nicht den zweiten Nachrichtenaufruf, daher werden hier nur neue (externe) Ziele verarbeitet. Es funktioniert OK für mich.
Ich habe meine Antwort auf die von Benjamin Piette gestützt, musste aber das Skript anpassen, da die in meinem Fall einzustellenden Links asynchron von einem anderen Javascript generiert wurden.
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];
}