如何在多个ViewController中复用WKNavigationDelegate的decidePolicyFor代码
嗨,这个问题我之前也碰到过——重复粘贴相同的代理代码简直是维护噩梦!下面给你分享几种实用的方案,轻松复用decidePolicyFor的逻辑:
方案1:创建独立的WKNavigationDelegate实现类
把代理逻辑抽成一个单独的类,所有ViewController都复用这个类作为导航代理,这是最直接的解耦方式。
步骤1:编写共享代理类
class SharedWebNavigationDelegate: NSObject, WKNavigationDelegate { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if let url = navigationAction.request.url, url.scheme == "tel" { UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) } else { decisionHandler(.allow) } } // 后续需要添加其他WKNavigationDelegate方法的话,直接在这里扩展就行 }
步骤2:在ViewController中使用
class DashboardViewController: UIViewController, WKUIDelegate { var webView: WKWebView! // 可以做成单例避免重复创建实例,或者每个VC单独持有 private let navigationDelegate = SharedWebNavigationDelegate() override func loadView() { let webConfiguration = WebViewUtils.getWKWebViewConfiguration() webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.uiDelegate = self webView.navigationDelegate = navigationDelegate // 替换成共享代理 view = webView } override func viewDidLoad() { super.viewDidLoad() webView.load(Constants.DASHBOARD) } }
方案2:使用协议扩展(Protocol Extension)
如果不想额外创建代理对象,协议扩展可以给遵循协议的类提供默认实现,灵活度拉满。
步骤1:定义协议并扩展
protocol WebViewNavigationHandling: WKNavigationDelegate {} extension WebViewNavigationHandling { // 默认实现decidePolicyFor方法 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if let url = navigationAction.request.url, url.scheme == "tel" { UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) } else { decisionHandler(.allow) } } }
步骤2:让ViewController遵循协议
class DashboardViewController: UIViewController, WKUIDelegate, WebViewNavigationHandling { var webView: WKWebView! override func loadView() { let webConfiguration = WebViewUtils.getWKWebViewConfiguration() webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.uiDelegate = self webView.navigationDelegate = self // 仍然用self,但协议扩展已经提供了默认实现 view = webView } override func viewDidLoad() { super.viewDidLoad() webView.load(Constants.DASHBOARD) } // 不需要再写decidePolicyFor!如果某个VC需要特殊逻辑,直接重写这个方法即可 }
方案3:创建基类ViewController
如果所有带WebView的VC都有大量共同逻辑(比如WebView初始化、基础配置),直接抽一个基类是最省心的。
步骤1:编写基类
class BaseWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate { var webView: WKWebView! override func loadView() { let webConfiguration = WebViewUtils.getWKWebViewConfiguration() webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.uiDelegate = self webView.navigationDelegate = self view = webView } // 共享的代理方法实现 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if let url = navigationAction.request.url, url.scheme == "tel" { UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) } else { decisionHandler(.allow) } } // 子类重写这个方法加载对应URL func loadTargetURL() {} override func viewDidLoad() { super.viewDidLoad() loadTargetURL() } }
步骤2:子类继承基类
class DashboardViewController: BaseWebViewController { override func loadTargetURL() { webView.load(Constants.DASHBOARD) } // 若需要特殊处理代理逻辑,直接重写decidePolicyFor即可 }
方案选择建议
- 独立代理类:适合代理逻辑和VC业务完全分离的场景,解耦性最好。
- 协议扩展:灵活度最高,不需要继承,想复用的VC只要遵循协议就行,还能随时重写方法。
- 基类继承:适合所有WebView VC有大量共同UI/逻辑的情况,一次性搞定所有重复代码。
内容的提问来源于stack exchange,提问作者love2code
相关产品推荐
相关产品推荐

