Swift中使用WKWebView创建PDF触发未知错误求助
macOS下WKWebView生成PDF报错WKErrorDomain Code=1的问题排查
问题场景
照搬示例代码在macOS中使用WKWebView的createPDF方法生成PDF时,始终触发失败回调,错误信息为:
failure(Error Domain=WKErrorDomain Code=1 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred.})
原代码如下:
private func CreatePDF(htmlString: String) { let width = ((21/2.54)*72) let height = ((29.7/2.54)*72) if let downloadDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { let savePath = downloadDir.appendingPathComponent("swiftPdf").appendingPathExtension("pdf") let webViewConfiguration = WKWebViewConfiguration() let webView = WKWebView(frame: .init(x: 0, y: 0, width: width, height: height), configuration: webViewConfiguration) let pdfConfiguration = WKPDFConfiguration() pdfConfiguration.rect = CGRect(x: 0.0, y: 0.0, width: width, height: height) webView.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL) DispatchQueue.main.asyncAfter(deadline: .now() + 1) { webView.createPDF(configuration: pdfConfiguration) { result in switch result { case .success(let data): do { try data.write(to: savePath) print("Successfully created and saved pdf…") } catch let error { print("Could not _save_ pdf: \(error)") } case .failure(let error): // <<-- always comes out here with error print("Could not create pdf: \(error)") } } } } }
核心问题与解决要点
- WKWebView必须加入视图层级:macOS平台要求WKWebView必须被添加到有效的视图或窗口中才能完成渲染和PDF生成,iOS支持离线渲染,但macOS不允许无层级的WebView进行渲染操作。
- 替换固定延迟为加载完成监听:
asyncAfter(deadline: .now() + 1)完全不可靠,页面加载时间受内容复杂度影响,必须通过WKNavigationDelegate监听页面加载完成事件,确保HTML完全渲染后再调用createPDF。 - 确保HTML内容合法完整:传入的
htmlString需要是标准HTML结构(包含<html>、<body>等核心标签),不完整或非法的HTML会导致渲染失败。
修改后的示例代码
// 让当前类遵循WKNavigationDelegate协议 class YourClass: NSObject, WKNavigationDelegate { private func createPDF(htmlString: String) { let width = ((21/2.54)*72) let height = ((29.7/2.54)*72) guard let downloadDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } let savePath = downloadDir.appendingPathComponent("swiftPdf").appendingPathExtension("pdf") let webViewConfiguration = WKWebViewConfiguration() let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: width, height: height), configuration: webViewConfiguration) webView.navigationDelegate = self // 将webView添加到当前视图层级(以NSViewController为例) self.view.addSubview(webView) webView.isHidden = true // 隐藏临时WebView,避免干扰界面 let pdfConfiguration = WKPDFConfiguration() pdfConfiguration.rect = CGRect(x: 0.0, y: 0.0, width: width, height: height) webView.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL) // 临时存储PDF生成所需的路径和配置 self.tempPDFInfo = (savePath, pdfConfiguration) } // 临时存储PDF路径与配置的变量 private var tempPDFInfo: (URL, WKPDFConfiguration)? // 监听页面加载完成事件 func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { guard let (savePath, pdfConfiguration) = tempPDFInfo else { return } webView.createPDF(configuration: pdfConfiguration) { result in switch result { case .success(let data): do { try data.write(to: savePath) print("Successfully created and saved pdf…") } catch let error { print("Could not save pdf: \(error)") } case .failure(let error): print("Could not create pdf: \(error)") } // 生成完成后移除临时WebView webView.removeFromSuperview() self.tempPDFInfo = nil } } // 监听页面加载失败事件 func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { print("Page load failed: \(error)") webView.removeFromSuperview() self.tempPDFInfo = nil } }
内容的提问来源于stack exchange,提问作者mihema
相关产品推荐
相关产品推荐

