I'm trying to convert the content of a WKWebView to a PDF file.
Before UIWebView was deprecated it was done this way:
webView.loadHTMLString("<h1>HELLO!</h1>", baseURL: nil)
let pdfData = webView.mainFrame.frameView.documentView.dataWithPDF(inside: webView.mainFrame.frameView.documentView.frame)
let document = PDFDocument(data: pdfData)
What I tried now:
webView.loadHTMLString("<h1>HELLO!</h1>", baseURL: nil)
let pdfData = self.webViewPDF.webFrame.frameView.documentView.dataWithPDF(inside: self.webViewPDF.webFrame.frameView.documentView.frame)
let document = PDFDocument(data: pdfData)
but got the following error instead:
[WKWebView webFrame]: unrecognized selector sent to instance.
[General] An uncaught exception was raised
Problem is WKWebView doesn't have mainFrame property...
Update. The solution bellow works great on iOS but I just noticed now that you are looking for a macOS solution instead. Please let me know if this solution might be of any help at all... sorry for the confusion ;)
You can now use the UIView.viewPrintFormatter() method (from the UIView class) for that:
Returns a print formatter for the receiving view.
When initiating a print job, you can call this method to obtain an appropriate view print formatter object for your view. You can use the formatter object to configure the page layout options for your view during printing.
For instance:
import UIKit
import WebKit
import PDFKit
extension WKWebView {
/// Exports a PDF document with this web view current contents.
/// Only call this function when the web view has **finished** loading.
func exportAsPDF() -> PDFDocument? {
guard self.isLoading == false else {
print("WKWebView still loading!")
return nil
}
let pdfData = createPDFData()
return PDFDocument(data: pdfData)
}
private func createPDFData() -> Data {
let oldBounds = self.bounds
var printBounds = self.bounds
printBounds.size.height = scrollView.contentSize.height
self.bounds = printBounds
var printableRect = printBounds
printableRect.origin = .zero
let printRenderer = UIPrintPageRenderer()
printRenderer.addPrintFormatter(self.viewPrintFormatter(), startingAtPageAt: 0)
printRenderer.setValue(NSValue(cgRect: UIScreen.main.bounds), forKey: "paperRect")
printRenderer.setValue(NSValue(cgRect: printableRect), forKey: "printableRect")
self.bounds = oldBounds
return printRenderer.generatePDFData()
}
}
and add the following helper method to UIPrintPageRenderer as well:
extension UIPrintPageRenderer {
func generatePDFData() -> Data {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, self.paperRect, nil)
self.prepare(forDrawingPages: NSMakeRange(0, self.numberOfPages))
let printRect = UIGraphicsGetPDFContextBounds()
for pdfPage in 0..<self.numberOfPages {
UIGraphicsBeginPDFPage()
self.drawPage(at: pdfPage, in: printRect)
}
UIGraphicsEndPDFContext()
return pdfData as Data
}
}
Finally, you can now save a PDF using the exportAsPDF() API defined above:
let webView: WKWebView = ...
let pdfDocument = webView.exportAsPDF()!
let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let pdfFile = docsDir.appendingPathComponent("WebViewPDF.pdf")
try! pdfDocument.write(to: pdfFile)