I'm working on webView and there is a challenging part for me capture the full screen depending on webView content.File exported successfully but not capture the whole screen data. Webview screen contains the large data like scrollable content.Can any one guide me how I can export full screen data depending on content.See my code below.
#IBAction func exportPDF(_ sender: UIButton) {
self.webview?.exportAsPdfFromWebView()
}
extension WKWebView {
// Call this function when WKWebView finish loading
func exportAsPdfFromWebView() -> String {
let pdfData = createPdfFile(printFormatter: self.viewPrintFormatter())
return self.saveWebViewPdf(data: pdfData)
}
func createPdfFile(printFormatter: UIViewPrintFormatter) -> NSMutableData {
let originalBounds = self.bounds
self.bounds = CGRect(x: originalBounds.origin.x, y: bounds.origin.y, width: self.bounds.size.width, height: self.scrollView.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.scrollView.contentSize.height)
let printPageRenderer = UIPrintPageRenderer()
printPageRenderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
printPageRenderer.setValue(NSValue(cgRect: UIScreen.main.bounds), forKey: "paperRect")
printPageRenderer.setValue(NSValue(cgRect: pdfPageFrame), forKey: "printableRect")
self.bounds = originalBounds
return printPageRenderer.generatePdfData()
}
// Save pdf file in document directory
func saveWebViewPdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("\(UUID().uuidString).pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
extension UIPrintPageRenderer {
func generatePdfData() -> NSMutableData {
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
}
}
I have a (to-do style) list app. the data is laid out in a diffable dataSource tableview with custom cells. I have a functioning dropdown menu and I'm trying to add a print button so the user can print out the list if they'd like (seems standard in a lot of list apps).
So far I have the following in the button's action:
let printController = UIPrintInteractionController.shared
printController.delegate = self
let printInfo = UIPrintInfo(dictionary: nil)
printInfo.outputType = .general
printInfo.jobName = "Print TableView"
printController.printInfo = printInfo
let tableViewPDF = self?.tableView.exportAsPdfFromTable()
printController.printingItem = tableViewPDF
let formatter = UIPrintFormatter()
formatter.perPageContentInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
printController.printFormatter = formatter
printController.present(animated: true, completionHandler: nil)
and the delegate:
extension ListTableViewController: UIPrintInteractionControllerDelegate {
func printInteractionControllerParentViewController(_ printInteractionController: UIPrintInteractionController) -> UIViewController? {
return self
}
}
As you can see I am trying to convert the tableview into a PDF and print that (something else might be better perhaps?). The .exportAsPdfFromTable() method on tableView is a UITableView extension as follows:
extension UITableView {
// Export pdf from UITableView and save pdf in drectory and return pdf file path
func exportAsPdfFromTable() -> String {
let originalBounds = self.bounds
self.bounds = CGRect(x:originalBounds.origin.x, y: originalBounds.origin.y, width: self.contentSize.width, height: self.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.contentSize.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
self.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
self.bounds = originalBounds
// Save pdf data
return self.saveTablePdf(data: pdfData)
}
// Save pdf file in document directory
func saveTablePdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("tablePdf.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
The result is the print controller being presented with a blank page. Any ideas on what I'm missing?
I currently have a UITableViewController that displays information I would like to have the option of saving as a PDF and printing later.
Currently I have the following function, but I cannot figure out where it is saving the file, hw can I make this easily accessible from the Files App under Downloads:
func pdfDataWithTableView(tableView: UITableView) {
let priorBounds = tableView.bounds
let fittedSize = tableView.sizeThatFits(CGSize(width:priorBounds.size.width, height:tableView.contentSize.height))
tableView.bounds = CGRect(x:0, y:0, width:fittedSize.width, height:fittedSize.height)
let pdfPageBounds = CGRect(x:0, y:0, width:tableView.frame.width, height:self.view.frame.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds,nil)
var pageOriginY: CGFloat = 0
while pageOriginY < fittedSize.height {
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
UIGraphicsGetCurrentContext()!.saveGState()
UIGraphicsGetCurrentContext()!.translateBy(x: 0, y: -pageOriginY)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsGetCurrentContext()!.restoreGState()
pageOriginY += pdfPageBounds.size.height
}
UIGraphicsEndPDFContext()
tableView.bounds = priorBounds
var docURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
docURL = docURL.appendingPathComponent("myDocument.pdf")
pdfData.write(to: docURL as URL, atomically: true)
}
I would like to create rows and columns from the data given(concept is to create like a invoice with static columns and dynamic rows which can be edited). The columns are static but the rows are dynamic. The thing is we can do it in tableview, but the scenario is to save it as pdf/excel/print. As i referred i've got to know that there is a process of converting the data to a html template and then converting it as a pdf to print. Can anyone help me with this?
func pdfDataWithTableView(tableView: UITableView)
{
let priorBounds = tableView.bounds
let fittedSize = tableView.sizeThatFits(CGSize(width:priorBounds.size.width, height:tableView.contentSize.height))
tableView.bounds = CGRect(x:0, y:0, width:fittedSize.width, height:fittedSize.height)
let pdfPageBounds = CGRect(x:0, y:0, width:tableView.frame.width, height:self.view.frame.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds,nil)
var pageOriginY: CGFloat = 0
while pageOriginY < fittedSize.height
{
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
UIGraphicsGetCurrentContext()!.saveGState()
UIGraphicsGetCurrentContext()!.translateBy(x: 0, y: -pageOriginY)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsGetCurrentContext()!.restoreGState()
pageOriginY += pdfPageBounds.size.height
}
UIGraphicsEndPDFContext()
tableView.bounds = priorBounds
var docURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
docURL = docURL.appendingPathComponent("myDocc.pdf")
pdfData.write(to: docURL as URL, atomically: true)
let data = try! Data(contentsOf: docURL)
let webView = UIWebView(frame: CGRect(x:20,y:20,width:view.frame.size.width-40, height:view.frame.size.height-40))
webView.load(data, mimeType: "application/pdf", textEncodingName:"", baseURL: docURL.deletingLastPathComponent())
view.addSubview(webView)
}
Here is the code i,ve used to convert the tableview data into a PDF. and
I have create pdf of tableview all cells. but i have create the pdf it does not show all data of tableview & also when pdf page height set according to cells data. I dont know how to resolve this issue. I am using this code for create pdf.please help me.
let _: NSData!
do {
let priorBounds = table_view.bounds
let fittedSize = table_view.sizeThatFits(CGSizeMake(priorBounds.size.width, .infinity))
table_view.bounds = CGRectMake(0, 0, fittedSize.width, fittedSize.height)
let pdfPageBounds = CGRectMake(0, 0, 612, 800)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
do {
var pageOriginY = 0
while pageOriginY < Int(fittedSize.height) {
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
CGContextSaveGState(UIGraphicsGetCurrentContext()!)
do {
CGContextTranslateCTM(UIGraphicsGetCurrentContext()!, 0, -CGFloat(pageOriginY))
table_view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
}
CGContextRestoreGState(UIGraphicsGetCurrentContext()!)
pageOriginY += Int(pdfPageBounds.size.height)
}
}
UIGraphicsEndPDFContext()
table_view.bounds = priorBounds
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + (filename as String)
debugPrint(documentsFileName)
pdfData.writeToFile(documentsFileName, atomically: true)
}
}
Try this.
func createPdfFromTableView()
{
let priorBounds: CGRect = self.tblView.bounds
let fittedSize: CGSize = self.tblView.sizeThatFits(CGSize(width: priorBounds.size.width, height: self.tblView.contentSize.height))
self.tblView.bounds = CGRect(x: 0, y: 0, width: fittedSize.width, height: fittedSize.height)
self.tblView.reloadData()
let pdfPageBounds: CGRect = CGRect(x: 0, y: 0, width: fittedSize.width, height: (fittedSize.height))
let pdfData: NSMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
self.tblView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let documentsFileName = documentDirectories! + "/" + "pdfName"
pdfData.write(toFile: documentsFileName, atomically: true)
print(documentsFileName)
}
One addition to Rajeskumar's answer below is as follows:
If your UITableView does not occupy the entire device screen with its original bounds, you might want to insert the following line:
self.tblview.bounds = priorBounds
after the render(in:) step. Otherwise, the table will be drawn on the device in a manner in which it will cover more space that originally intended.
Above answers did not work for creating PDF after tableview is scrolled at the bottom.
While reloading tableview(reloadData()) after scrolling, correct PDF is not generated and removing, it won't generate PDF for unloaded items.
So I have added extra condition and refactored as below:
extension UITableView {
func convertToPDF() -> Data? {
let priorBounds = self.bounds
setBoundsForAllItems()
self.layoutIfNeeded()
let pdfData = createPDF()
self.bounds = priorBounds
return pdfData.copy() as? Data
}
private func getContentFrame() -> CGRect {
return CGRect(x: 0, y: 0, width: self.contentSize.width, height: self.contentSize.height)
}
private func createPDF() -> NSMutableData {
let pdfPageBounds: CGRect = getContentFrame()
let pdfData: NSMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
return pdfData
}
private func setBoundsForAllItems() {
if self.isEndOfTheScroll() {
self.bounds = getContentFrame()
} else {
self.bounds = getContentFrame()
self.reloadData()
}
}
private func isEndOfTheScroll() -> Bool {
let contentYoffset = contentOffset.y
let distanceFromBottom = contentSize.height - contentYoffset
return distanceFromBottom < frame.size.height
}
}
Create PDF of tableview using this way
#IBAction func onClickBtnGeneratePDF(_ sender: Any) {
if enterPDFNAME.text?.isEmpty() == true{
Utility.showToast(message: "Please Enter PDF name")
}else{
self.viewOverlaySavePDF.isHidden = true
Utility.showLoading()
let pdfFilePath = tableview.exportAsPdfFromTable(pdfName: enterPDFNAME.text!)
if pdfFilePath != nil {
self.enterPDFNAME.text = ""
Utility.showToast(message: "Your PDF file saved")
Utility.hideLoading()
}
print(pdfFilePath)
}
}
}
extension UITableView {
// Export pdf from UITableView and save pdf in drectory and return pdf file path
func exportAsPdfFromTable(pdfName:String) -> String {
/* let html = "<b>Hello <i>World!</i></b> <p>Generate PDF file from HTML in Swift</p>"
let fmt = UIMarkupTextPrintFormatter(markupText: html)
// 2. Assign print formatter to UIPrintPageRenderer
let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAt: 0)
// 3. Assign paperRect and printableRect
let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = page.insetBy(dx: 0, dy: 0)
render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
// 4. Create PDF context and draw
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)
for i in 1...render.numberOfPages {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)
}
UIGraphicsEndPDFContext();
*/
let originalBounds = self.bounds
self.bounds = CGRect(x:originalBounds.origin.x, y: originalBounds.origin.y, width: self.contentSize.width, height: self.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.contentSize.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
self.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
self.bounds = originalBounds
// Save pdf data
return self.saveTablePdf(data: pdfData,name:pdfName)
}
// Save pdf file in document directory
func saveTablePdf(data: NSMutableData,name:String) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("\(name).pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
Only one solution for exporting all tableView items: make snapshot of every part of tableview as an image -> merge them into an imageView -> render imageView to pdf.
NOTE: It's the same way with UIScrollView & UICollectionView
func exportPDF() {
// 1. estimate tableview contentsize
UIGraphicsBeginImageContextWithOptions(tableView.contentSize, false, 0)
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
// 2. calculate number of part of tableview, loop and get snapshot
let iterationCount = Int(ceil(tableView.contentSize.height / tableView.bounds.size.height))
for i in 0..<iterationCount {
tableView.setContentOffset(CGPoint(x: 0, y: Int(tableView.bounds.size.height) * i), animated: false)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
}
// 3. merge and make UIImageView from snapshot
let image = UIGraphicsGetImageFromCurrentImageContext()
let pdfImageView = UIImageView(image: image)
//4. render imageView to PDF
let pdfData: NSMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfImageView.bounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfImageView.bounds, nil)
pdfImageView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let documentsFileName = documentDirectories! + "/" + "MyPDF.pdf"
pdfData.write(toFile: documentsFileName, atomically: true)
// 5. We have pdf file at documentsFileName
}