Create Pdf from UICollectionView full content? - ios

I need to create a PDF from UICollectionView full content. I am currently able to create a pdf from UICollectionView Visible part. Please, help me i want to display whole content of collection view.

This is how I solved it. (Swift 3.x)
class PdfTools {
func generatePdfFromCollectionView(_ collectionView: UICollectionView?, filename:String, success:(String) -> ()) {
guard let collectionView = collectionView else {
return
}
let pdfData = NSMutableData()
let contentArea = CGRect(
x: 0,
y: 0,
width: collectionView.contentSize.width,
height: collectionView.contentSize.height
)
collectionView.frame = contentArea
UIGraphicsBeginPDFContextToData(pdfData, contentArea, nil)
UIGraphicsBeginPDFPage()
collectionView.drawHierarchy(in: collectionView.bounds, afterScreenUpdates: true)
UIGraphicsEndPDFContext()
if let filepath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let fileFullPath = filepath + "/" + filename
if pdfData.write(toFile: fileFullPath, atomically: true) {
success(fileFullPath)
}
}
}
}
This is how you use it.
let pdfTool = PdfTool()
pdfTool.generatePdfFromCollectionView(self.collectionView, filename: "myFancy.pdf") { (filename) in
// use your pdf here
}

Related

Trying to add functionality to be able to air Print a UITableView

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?

pdf from tableview with 100 cells

I am using below code to generate pdf from my tableview , but it generates pdf from visible cells only.
how to generate pdf from complete tableview
eg : Visible cells
cell1
cell2
.
.
.
.
.
cell10
non visible cell to current view
cell11
cell12
.
.
.
.
cell20
on generating pdf it only create pdf of visible cells
I need to create all tableview cell1 to cell20 in pdf.
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 ""
}
}
}
to use it
let pdfFilePath = self.tableView.exportAsPdfFromTable()
print(pdfFilePath)

How do I generate a PDF containing views with dimensions larger than device screen?

I have several UIViews with clear background color, and black border outlines that all resize based on values I enter in length, width, and height text fields. These views are subviews of a UIScrollView. After the views resize I create a PDF from the contents of the scroll view resulting in vectorized outlines that I can use and manipulate in Adobe Illustrator.
This process works fine when I use small dimensions for the views (i.e. 15 x 15), but when I use more ideal dimensions for my purpose (i.e. 3000 x 3000) the views extend off the bounds of the screen and the resulting PDF generated contains only a small portion of the views.
Ideally I need to be able to create much larger views and still be able to generate a PDF containing them. I'm envisioning a canvas that resizes (zooms out?) based on its contents and retains the scale of its subviews so that the resulting views in the PDF will be to-scale when viewed in Adobe Illustrator. This is my code so far:
override func viewDidLoad() {
super.viewDidLoad()
lengthTextField.delegate = self
widthTextField.delegate = self
heightTextField.delegate = self
scrollView.delegate = self
scrollView.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth,UIView.AutoresizingMask.flexibleHeight]
scrollView.minimumZoomScale = 1
scrollView.maximumZoomScale = 50
scrollView.zoomScale = 1
self.setupGestureRecognizer()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: 5000, height: 5000)
}
func setupGestureRecognizer() {
}
#IBAction func viewPDF(_ sender: Any) {
createPDFfrom(aView: self.scrollView.subviews[0], saveToDocumentsWithFileName: "MC.pdf")
// Create and add a PDFView to the view hierarchy.
let pdfView = PDFView(frame: self.scrollView.subviews[0].bounds)
pdfView.autoScales = true
view.addSubview(pdfView)
// Create a PDFDocument object and set it as PDFView's document to load the document in that view.
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath = (documentsDirectory as NSString).appendingPathComponent("MC.pdf") as String
let pdfDocument = PDFDocument(url: URL(fileURLWithPath: filePath))!
pdfView.document = pdfDocument
let document = NSData(contentsOfFile: filePath)
let vc = UIActivityViewController(activityItems: [document as Any], applicationActivities: nil)
self.present(vc, animated: true, completion: nil)
}
func createPDFfrom(aView: UIView, saveToDocumentsWithFileName fileName: String)
{
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil)
UIGraphicsBeginPDFPage()
guard let pdfContext = UIGraphicsGetCurrentContext() else { return }
aView.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + fileName
debugPrint(documentsFileName)
pdfData.write(toFile: documentsFileName, atomically: true)
}
}
#IBAction func updateDimensions(_ sender: Any) {
guard let length = NumberFormatter().number(from:
lengthTextField.text ?? "") else { return }
guard let width = NumberFormatter().number(from:
widthTextField.text ?? "") else { return }
guard let height = NumberFormatter().number(from:
heightTextField.text ?? "") else { return }
let flapHeight = CGFloat(truncating: width)/2
let lengthFloat = CGFloat(truncating: length)
let widthFloat = CGFloat(truncating: width)
let heightFloat = CGFloat(truncating: height)
UIView.animate(withDuration: 0.3) {
self.faceAWidthConstraint.constant = lengthFloat
self.faceAHeightConstraint.constant = heightFloat
self.faceBWidthConstraint.constant = widthFloat
self.faceA1HeightConstraint.constant = flapHeight
self.view.layoutIfNeeded()
}
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return scrollView.subviews[0]
}
}
I think if you use UIGraphicsPDFRenderer this might work better:
let url = URL(fileURLWithPath: filePath)
let pdfGenerator = UIGraphicsPDFRenderer(bounds: .init(x: 0, y: 0, width: 612, height: 792))
pdfGenerator.writePDF(to: url) { context in
let cgContext = context.cgContext
aView.layer.render(in: cgContext)
}
If the views are still extending outside of the PDF's bounds then you could scale the context:
cgContext.scaleBy(x: 0.25, y:0.25)

Create a PDF file from a UIScrollView in Swift

I want to create a PDF file from the contents of a UIScrollView.
func createPdfFromView(aView: UIView, saveToDocumentsWithFileName fileName: String) {
let pdfData = NSMutableData()
let height = 1754.0
let width = 1240.0
UIGraphicsBeginPDFContextToData(pdfData, CGRect(x:-30, y:15,width:width,height:height) , nil)
UIGraphicsBeginPDFPage()
guard let pdfContext = UIGraphicsGetCurrentContext() else { return }
aView.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + fileName
debugPrint(documentsFileName)
pdfData.write(toFile: documentsFileName, atomically: true)
}
}
I would expect that the code generates a PDF oriented on the size of the content in the scrollview.
The code currently only generates a static PDF. If the content in the scrollview is bigger than the PDF page, the content is cut off.
func createPdfFromView()
{
// Set frame as per content of view
self.spreadsheetView.frame = CGRect(x: 0, y: 0, width: self.spreadsheetView.contentSize.width, height: self.spreadsheetView.contentSize.height)
let pdfPageBounds: CGRect = self.spreadsheetView.frame
let pdfData: NSMutableData = NSMutableData()
// Rendering spreadsheetView
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
self.spreadsheetView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let documentsFileName = documentDirectories! + "/" + "pdfName.pdf"
pdfData.write(toFile: documentsFileName, atomically: true)
print(documentsFileName)
// Reset spreadsheetView
self.spreadsheetView.frame = CGRect(x: 0, y: 64, width: self.view.frame.width, height: self.view.frame.height)
self.spreadsheetView.layoutSubviews()
self.spreadsheetView.layoutIfNeeded()
self.spreadsheetView.reloadData()}
Hope it will help you to create PDF from the spreadsheet.
Ref. Github code Link: SpreadsheetView
I could achieve this using this Cocoapods framework. In my case I wanted to make a PDF from Spreadsheet , so that this framework help me to add table with different column size and many more features which I had wanted from my data.
https://cocoapods.org/pods/SimplePDF

Create pdf of tableview all data

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
}

Resources