此答案建立在此线程中的出色答案之上。
使用 WKWebView 的上下文菜单的挑战是:
- 只能在 WKWebView 的子类中操作
- WebKit 不会公开有关用户右键单击的 HTML 元素的任何信息。因此,关于元素的信息必须在 JavaScript 中被截获并返回到 Swift 中。
通过在渲染之前将 JavaScript 注入页面,然后在 Swift 中建立回调来拦截和查找有关用户单击的元素的信息。这是我为此编写的课程。它适用于 WKWebView 的配置对象。它还假设一次只有一个上下文菜单可用:
class GlobalScriptMessageHandler: NSObject, WKScriptMessageHandler {
public private(set) static var instance = GlobalScriptMessageHandler()
public private(set) var contextMenu_nodeName: String?
public private(set) var contextMenu_nodeId: String?
public private(set) var contextMenu_hrefNodeName: String?
public private(set) var contextMenu_hrefNodeId: String?
public private(set) var contextMenu_href: String?
static private var WHOLE_PAGE_SCRIPT = """
window.oncontextmenu = (event) => {
var target = event.target
var href = target.href
var parentElement = target
while (href == null && parentElement.parentElement != null) {
parentElement = parentElement.parentElement
href = parentElement.href
}
if (href == null) {
parentElement = null;
}
window.webkit.messageHandlers.oncontextmenu.postMessage({
nodeName: target.nodeName,
id: target.id,
hrefNodeName: parentElement?.nodeName,
hrefId: parentElement?.id,
href
});
}
"""
private override init() {
super.init()
}
public func ensureHandles(configuration: WKWebViewConfiguration) {
var alreadyHandling = false
for userScript in configuration.userContentController.userScripts {
if userScript.source == GlobalScriptMessageHandler.WHOLE_PAGE_SCRIPT {
alreadyHandling = true
}
}
if !alreadyHandling {
let userContentController = configuration.userContentController
userContentController.add(self, name: "oncontextmenu")
let userScript = WKUserScript(source: GlobalScriptMessageHandler.WHOLE_PAGE_SCRIPT, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(userScript)
}
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if let body = message.body as? NSDictionary {
contextMenu_nodeName = body["nodeName"] as? String
contextMenu_nodeId = body["id"] as? String
contextMenu_hrefNodeName = body["hrefNodeName"] as? String
contextMenu_hrefNodeId = body["hrefId"] as? String
contextMenu_href = body["href"] as? String
}
}
接下来,要在 WKWebView 中启用此功能,您必须对其进行子类化并在构造函数中调用 GlobalScriptMessageHandler.instance.ensureHandles:
class WebView: WKWebView {
public var webViewDelegate: WebViewDelegate?
init() {
super.init(frame: CGRect(), configuration: WKWebViewConfiguration())
GlobalScriptMessageHandler.instance.ensureHandles(configuration: self.configuration)
}
最后,(正如其他答案所指出的那样)您覆盖上下文菜单处理程序。在这种情况下,我更改了“打开链接”菜单项的目标操作。您可以根据需要更改它们:
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) {
for index in 0...(menu.items.count - 1) {
let menuItem = menu.items[index]
if menuItem.identifier?.rawValue == "WKMenuItemIdentifierOpenLink" {
menuItem.action = #selector(openLink(_:))
menuItem.target = self
然后,在您处理菜单项的方法中,使用 GlobalScriptMessageHandler.instance.contextMenu_href 获取用户右键单击的 URL:
@objc func openLink(_ sender: AnyObject) {
if let url = GlobalScriptMessageHandler.instance.contextMenu_href {
let url = URL(string: url)!
self.load(URLRequest(url: url))
}
}