Этот ответ основан на отличных ответах в этой теме.
Проблемы при работе с контекстным меню 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)
}
Наконец, (как указывалось в других ответах) вы переопределяете обработчик контекстного меню. В этом случае я изменил действие в target для пункта меню Open Link. Вы можете изменить их по своему усмотрению:
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))
}
}
person
Andrew Rondeau
schedule
27.03.2021