I am developing an applicat开发者_JAVA百科ion in Cocoa, and I would like to know if there is a way/how to make any link clicked in my webview to launch in Safari (or the default browser). Thanks in advance!
I just ran into this problem myself, and solved it this way:
My main controller object was set as the policyDelegate for the WebView.
Then implement this method:
- (void)webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request frame:(WebFrame *)frame
decisionListener:(id < WebPolicyDecisionListener >)listener
{
NSString *host = [[request URL] host];
if (host) {
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
} else {
[listener use];
}
}
I needed to have the check for the host there, because otherwise when I load the initial content of the WebView (just a static html page in my project), it was being launched in Safari. Now, just links with actual hosts (external links) get sent to Safari, which is the behavior I wanted. I think I could also have not set the policyDelegate until after the initial page was loaded.
I came across the same problem and the solution I have found is not too intuitive.
As a previous answer indicated, you have to use the WebPolicyDelegate
protocol, defined in <WebKit/WebPolicyDelegate.h>
. It is an informal protocol, so you cannot write @interface MyDelegate : NSObject <WebPolicyDelegate>
.
#import <WebKit/WebPolicyDelegate.h>
(or the whole WebKit.h) and implement the webView:decidePolicyForNavigationAction:request:frame:decisionListener:
method as follows:
- (void)webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id <WebPolicyDecisionListener>)listener
{
if ([actionInformation objectForKey:WebActionElementKey]) {
[listener ignore];
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
}
else {
[listener use];
}
}
Then set your class as PolicyDelegate for the WebView.
Logic dictates you should use a different key from the actionInformation
dictionary, namely WebActionNavigationTypeKey
, whose value should be an enum that can include WebNavigationTypeLinkClicked
. Unfortunately, the values I have seen look random and far beyond the correct range (6 decimal digits integers while the enum goes from 0 to 5).
But, there is something else to check, the WebActionElementKey
, that returns the HTML entity that originates the navigation action. When loading data in the frame from code, its value is nil
whereas when user clicks a link, its value is the (parsed) <a>
entity. Checking it for non-nil
value does the trick.
Thanks for the tip! I had to route only the external links to Safari. To get a name anchor (with #) to display in the webView I did following:
- (void)webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id <WebPolicyDecisionListener>)listener
{
NSDictionary *info = actionInformation;
NSURL *url = [info objectForKey:WebActionOriginalURLKey];
NSString *path = [url absoluteString];
NSRange range;
range = [path rangeOfString:@"file://"];
if (range.location != 0) {
// open in Safari ö
[listener ignore];
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
}
else {
// open in webView because link starts with file://...
[listener use];
}
}
Swift
Swift example below:
import Foundation
import Cocoa
import WebKit
class WebViewManager: WebView {
private var _urlHome = NSURL(string: "http://www.apple.com")
override init() {
super.init()
}
override init(frame: NSRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
//Sets current object as the receiver of policy delegate
self.policyDelegate = self
//Load homepage
self.loadHomePage()
}
func loadHomePage() {
let request = NSURLRequest(URL: self._urlHome!);
self.mainFrame.loadRequest(request)
}
override func webView(webView: WebView!, decidePolicyForNewWindowAction actionInformation: [NSObject : AnyObject]!, request: NSURLRequest!, newFrameName frameName: String!, decisionListener listener: WebPolicyDecisionListener!) {
println(__LINE__)
}
override func webView(webView: WebView!, decidePolicyForNavigationAction actionInformation: [NSObject : AnyObject]!, request: NSURLRequest!, frame: WebFrame!, decisionListener listener: WebPolicyDecisionListener!) {
println(request.URL.host)
if request.URL.host == self._urlHome?.host {
listener.use()
}else {
NSWorkspace.sharedWorkspace().openURL(request.URL)
}
}
}
--
Do not forget to set Custom Class on Inspector too, choose WebViewManager, otherwise it wouldn't work.
If you want clicks opened on external browser, use this
override func webView(webView: WebView!, decidePolicyForNavigationAction actionInformation: [NSObject : AnyObject]!, request: NSURLRequest!, frame: WebFrame!, decisionListener listener: WebPolicyDecisionListener!) {
//Check action action type, if click, open an external view
let info:NSDictionary = actionInformation as NSDictionary
let mode:Int = info.valueForKey(WebActionNavigationTypeKey) as Int
if mode == 0 {
NSWorkspace.sharedWorkspace().openURL(request.URL)
self.mainFrame.reload()
}else{
listener.use()
}
}
精彩评论