开发者

Opening WebView links in Safari

开发者 https://www.devze.com 2023-02-01 18:01 出处:网络
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 ad

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.

Opening WebView links in Safari

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()
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消