WKWebView加载网页内容时didFinish代理方法被多次调用问题咨询
WKWebView didFinish代理方法多次调用问题解决方案
问题诱因
- 代理协议配置错误:当前扩展仅遵守了
WKUIDelegate与废弃的UIWebViewDelegate,而用到的didStartProvisionalNavigation、didFinish都属于WKNavigationDelegate协议方法,若未给WKWebView的navigationDelegate属性赋值为当前控制器,会导致页面开始加载的回调不触发,ActivityCount只减不增,计数逻辑完全失效。 - 网页多导航行为触发:页面301/302重定向、内嵌iframe加载、JS动态触发跳转、异步资源加载等行为都会触发独立的导航生命周期,每次导航完成都会调用一次
didFinish,属于WKWebView的正常机制。 - 缺少加载失败兜底逻辑:未实现页面加载失败的代理方法,若加载过程中出现错误,
ActivityCount不会扣减,会导致计数永久大于0,加载弹窗无法关闭。 - 计数变量作用域风险:若
ActivityCount为全局变量,多个Webview实例共用会出现计数混乱。
修复方案
- 修正代理协议配置
将扩展的协议声明替换为WKNavigationDelegate+WKUIDelegate,移除废弃的UIWebViewDelegate,同时初始化Webview时赋值navigationDelegate:
// 初始化webview时添加这行 webview.navigationDelegate = self
- 补充加载失败的计数逻辑,将计数变量改为当前控制器的私有属性,避免全局污染:
extension PresentWebview: WKUIDelegate, WKNavigationDelegate { func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { // activityCount改为当前控制器的私有属性,不要用全局变量 activityCount += 1 } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { activityCount -= 1 checkLoadingStatus() } // 补充导航过程中加载失败的回调 func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { activityCount -= 1 checkLoadingStatus() } // 补充页面开始加载就失败的回调 func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { activityCount -= 1 checkLoadingStatus() } // 抽离加载状态判断逻辑 private func checkLoadingStatus() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in guard let self = self, self.activityCount == 0 else { return } print("This is called") self.loadingOverlay.hideOverlayView() } } }
- 可选优化:如果只需要监听主页面加载完成,不需要统计iframe等子资源的加载,可以在计数前判断当前加载的URL是否为初始加载的主URL:
// 在控制器内保存初始加载的URL private var originalURL: URL? func showWebview() { self.webview.addSubview(loadingOverlay.activityIndicator) self.loadingOverlay.showOverlay(view: (UIApplication.shared.keyWindow!)) originalURL = URL(string: data ?? "") guard let url = originalURL else { return } let request = URLRequest(url: url) self.webview.load(request) } // 在didStartProvisionalNavigation里加判断 func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { guard webView.url == originalURL else { return } activityCount += 1 }
调试截图

内容的提问来源于stack exchange,提问作者Manish Kumar
相关产品推荐
相关产品推荐

