自定义WKWebView中如何修改长按链接菜单并添加自定义功能
解决方案
要实现长按链接时打印目标URL、移除系统默认的“在Safari中打开链接”选项并添加自定义选项,需要从JS事件拦截和WKWebView上下文菜单自定义两方面入手,以下是具体修改步骤:
1. 让NavigationState遵守WKUIDelegate
通过WKUIDelegate接管WebView的上下文菜单配置,修改类声明:
class NavigationState : NSObject, ObservableObject, WKNavigationDelegate, WKUIDelegate { // 原有代码保持不变 }
2. 修改WKWebView初始化逻辑
创建WebView时设置uiDelegate,同时添加消息处理器接收JS传递的链接信息:
@discardableResult func createNewWebView(withRequest request: URLRequest) -> WKWebView { let config = WKWebViewConfiguration() // 添加消息处理器,接收JS传递的链接数据 config.userContentController.add(self, name: "linkLongPress") let wv = WKWebView(frame: .zero, configuration: config) wv.navigationDelegate = self wv.uiDelegate = self // 设置UIDelegate webViews.append(wv) selectedWebView = wv wv.load(request) return wv }
3. 替换JS脚本,拦截所有链接的长按事件
将原有针对特定按钮的JS替换为监听所有<a>标签的长按事件,阻止默认行为并传递链接URL给原生:
// 在WebView的updateUIView方法中替换原有script let script = """ document.querySelectorAll('a').forEach(link => { link.addEventListener('contextmenu', function(event) { event.preventDefault(); // 将链接的href传递给原生 window.webkit.messageHandlers.linkLongPress.postMessage(this.href); }); }); """ let userScript = WKUserScript(source: script, injectionTime: .atDocumentEnd, forMainFrameOnly: true) webView.configuration.userContentController.addUserScript(userScript)
4. 存储长按的链接URL
在NavigationState中新增属性存储当前长按的链接:
class NavigationState : NSObject, ObservableObject, WKNavigationDelegate, WKUIDelegate { // 原有属性 @Published var longPressedLinkURL: URL? // 新增:存储长按的链接URL // 原有方法 }
5. 处理JS传递的链接消息
修改消息处理方法,接收并存储链接URL,同时打印:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "buttonLongPress" { print("Pressed") buttonLongPressPublisher.send() } else if message.name == "linkLongPress" { if let urlString = message.body as? String, let url = URL(string: urlString) { longPressedLinkURL = url // 打印长按的链接URL print("Long-pressed link URL: \(url)") } } }
6. 自定义上下文菜单:移除系统选项+添加自定义选项
实现WKUIDelegate方法,过滤系统默认选项并添加自定义菜单项:
func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) { // 获取系统默认菜单配置 webView.defaultContextMenuConfigurationForElement(elementInfo) { defaultConfig in var modifiedActions = [UIMenuElement]() // 过滤掉"在Safari中打开链接"选项 if let defaultActions = defaultConfig?.menuItems { modifiedActions = defaultActions.filter { item in return item.title != NSLocalizedString("Open in Safari", comment: "") } } // 添加自定义菜单选项 let customAction = UIAction(title: "我的自定义操作", image: UIImage(systemName: "star.fill")) { [weak self] _ in if let url = self?.longPressedLinkURL { // 自定义操作逻辑:比如打印URL或其他处理 print("自定义操作触发,链接URL:\(url)") } } modifiedActions.append(customAction) // 构建新的菜单配置 let modifiedConfig = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in UIMenu(title: "", children: modifiedActions) } completionHandler(modifiedConfig) } }
7. 清理冗余代码
如果不再需要原有特定按钮的长按逻辑,可以删除buttonLongPressPublisher及对应JS脚本。
关键说明
- JS拦截
contextmenu事件,既可以传递链接信息给原生,也能配合系统菜单自定义控制行为。 - 通过
WKUIDelegate的上下文菜单配置方法,实现系统选项过滤和自定义菜单项添加。 - 存储的长按链接URL可直接用于自定义操作的逻辑处理。
内容的提问来源于stack exchange,提问作者anish1
相关产品推荐
相关产品推荐

