iOS Swift中WKWebView的goBack()方法无法正常工作问题排查
问题诊断
你的回退功能异常根源在于:拦截了.backForward类型的原生回退导航,并用load(_:)方法重新发起请求。WKWebView的load(_:)会在历史栈顶部新增页面,而非回退到历史栈的上一个页面——原本的回退操作应该移除栈顶页面,但你取消原生回退后,新增了相同URL的页面到栈顶,导致历史栈持续膨胀:
- 初始栈:[页面A]
- 加载页面B后:[A, B]
- 第一次点击返回:拦截回退到A的请求,取消后加载带自定义头的A,栈变为[A, B, A],
canGoBack仍为true - 第二次点击返回:拦截回退到B的请求,取消后加载带自定义头的B,栈变为[A, B, A, B],
canGoBack依旧为true - 多次操作后栈被耗尽,
canGoBack变为false,才触发关闭逻辑
解决方案
方案一:全局自定义请求头(最简方案)
如果业务场景允许,直接通过WKWebViewConfiguration设置全局附加请求头,所有请求(包括回退导航)会自动携带自定义头,无需拦截导航:
// 初始化WKWebView时配置全局头 let webConfig = WKWebViewConfiguration() webConfig.additionalHTTPHeaders = [ "header_key1": "header_value1", "header_key2": "header_value2" ] let webView = WKWebView(frame: view.bounds, configuration: webConfig)
然后删除decidePolicyFor中针对.backForward类型的拦截逻辑,回退方法保留原逻辑即可:
func goBackInWebView(_ webView: WKWebView?) { if webView?.canGoBack ?? false { webView?.goBack() return } close() }
注意:该方案适用于多数GET请求场景,若存在POST请求或网页主动修改请求头的情况,需结合其他方式调整。
方案二:手动管理页面层级(兼容复杂场景)
若全局头无法满足需求(如动态修改头、跨域/POST请求),则通过手动维护页面层级替代WKWebView的原生历史栈判断:
步骤1:添加页面层级变量
在WebView控制器中添加变量记录当前页面层级:
private var pageLevel = 1 // 初始页面层级为1
步骤2:修改decidePolicyFor逻辑
只拦截非回退类型的导航,同时更新页面层级:
override func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { self.currentLoadedUrl = navigationAction.request.url let requestHeaders = navigationAction.request.allHTTPHeaderFields ?? [:] // 仅处理非回退类型的导航(点击链接、JS跳转等) if navigationAction.navigationType != .backForward, requestHeaders["header_key1"] == nil || requestHeaders["header_key2"] == nil, let currentUrl = navigationAction.request.url, currentUrl != self.getInitialUrl() { decisionHandler(.cancel) var request = URLRequest(url: currentUrl) request.setValue("header_value1", forHTTPHeaderField: "header_key1") request.setValue("header_value2", forHTTPHeaderField: "header_key2") webView.load(request) pageLevel += 1 // 加载新页面,层级加1 } else { decisionHandler(.allow) } }
步骤3:修改回退方法逻辑
通过页面层级判断是否回退或关闭控制器:
func goBackInWebView(_ webView: WKWebView?) { pageLevel -= 1 if pageLevel > 0 { // 获取历史栈中上一个页面的URL,构造带自定义头的请求加载 guard let backUrl = webView?.backForwardList.backItem?.url else { close() return } var request = URLRequest(url: backUrl) request.setValue("header_value1", forHTTPHeaderField: "header_key1") request.setValue("header_value2", forHTTPHeaderField: "header_key2") webView?.load(request) } else { close() } }
额外处理:若存在页面刷新(navigationType为.reload)或JS跳转的情况,需根据实际场景调整pageLevel的增减逻辑,避免层级计算错误。
内容的提问来源于stack exchange,提问作者Krima Shah
相关产品推荐
相关产品推荐

