How to Convert UIView to PDF within iOS?

54,272

Solution 1

Note that the following method creates just a bitmap of the view; it does not create actual typography.

(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [NSMutableData data];

    // Points the pdf converter to the mutable data object and to the UIView to be converted
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();


    // draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData

    [aView.layer renderInContext:pdfContext];

    // remove PDF rendering context
    UIGraphicsEndPDFContext();

    // Retrieves the document directories from the iOS device
    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];

    // instructs the mutable data object to write its context to a file on disk
    [pdfData writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
}

Also make sure you import: QuartzCore/QuartzCore.h

Solution 2

Additionally, if anyone is interested, here is the Swift 3 code:

func createPdfFromView(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)
    }
}

Solution 3

If someone is interested, here's Swift 2.1 code:

    func createPdfFromView(aView: UIView, saveToDocumentsWithFileName fileName: String)
    {
        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil)
        UIGraphicsBeginPDFPage()

        guard let pdfContext = UIGraphicsGetCurrentContext() else { return }

        aView.layer.renderInContext(pdfContext)
        UIGraphicsEndPDFContext()

        if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
            let documentsFileName = documentDirectories + "/" + fileName
            debugPrint(documentsFileName)
            pdfData.writeToFile(documentsFileName, atomically: true)
        }
    }

Solution 4

A super easy way to create a PDF from UIView is using UIView Extension

Swift 4.2

extension UIView {

  // Export pdf from Save pdf in drectory and return pdf file path
  func exportAsPdfFromView() -> String {

      let pdfPageFrame = self.bounds
      let pdfData = NSMutableData()
      UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
      UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
      guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
      self.layer.render(in: pdfContext)
      UIGraphicsEndPDFContext()
      return self.saveViewPdf(data: pdfData)

  }

  // Save pdf file in document directory
  func saveViewPdf(data: NSMutableData) -> String {  
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let docDirectoryPath = paths[0]
    let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
    if data.write(to: pdfPath, atomically: true) {
        return pdfPath.path
    } else {
        return ""
    }
  }
}

Credit: http://www.swiftdevcenter.com/create-pdf-from-uiview-wkwebview-and-uitableview/

Solution 5

With Swift 5 / iOS 12, you can combine CALayer's render(in:) method with UIGraphicsPDFRenderer's writePDF(to:withActions:) method in order to create a PDF file from a UIView instance.


The following Playground sample code shows how to use render(in:) and writePDF(to:withActions:):

import UIKit
import PlaygroundSupport

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .orange
let subView = UIView(frame: CGRect(x: 20, y: 20, width: 40, height: 60))
subView.backgroundColor = .magenta
view.addSubview(subView)

let outputFileURL = PlaygroundSupport.playgroundSharedDataDirectory.appendingPathComponent("MyPDF.pdf")
let pdfRenderer = UIGraphicsPDFRenderer(bounds: view.bounds)

do {
    try pdfRenderer.writePDF(to: outputFileURL, withActions: { context in
        context.beginPage()
        view.layer.render(in: context.cgContext)
    })
} catch {
    print("Could not create PDF file: \(error)")
}

Note: in order to use playgroundSharedDataDirectory in your Playground, you first need to create a folder called "Shared Playground Data" in you macOS "Documents" folder.


The UIViewController subclass complete implementation below shows a possible way to refactor the previous example for an iOS app:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        view.backgroundColor = .orange
        let subView = UIView(frame: CGRect(x: 20, y: 20, width: 40, height: 60))
        subView.backgroundColor = .magenta
        view.addSubview(subView)

        createPDF(from: view)
    }

    func createPDF(from view: UIView) {
        let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let outputFileURL = documentDirectory.appendingPathComponent("MyPDF.pdf")
        print("URL:", outputFileURL) // When running on simulator, use the given path to retrieve the PDF file

        let pdfRenderer = UIGraphicsPDFRenderer(bounds: view.bounds)

        do {
            try pdfRenderer.writePDF(to: outputFileURL, withActions: { context in
                context.beginPage()
                view.layer.render(in: context.cgContext)
            })
        } catch {
            print("Could not create PDF file: \(error)")
        }
    }

}
Share:
54,272
Cullen SUN
Author by

Cullen SUN

It is fun, coding.

Updated on December 31, 2020

Comments

  • Cullen SUN
    Cullen SUN over 3 years

    There are a lot of resources about how to display a PDF in an App's UIView. What I am working on now is to create a PDF from UIViews.

    For example, I have a UIView, with subviews like Textviews, UILabels, UIImages, so how can I convert a big UIView as a whole including all its subviews and subsubviews to a PDF?

    I have checked Apple's iOS reference. However, it only talks about writing pieces of text/image to a PDF file.

    The problem I am facing is that the content I want to write to a file as PDF is a lot. If I write them to the PDF piece by piece, it is going to be huge work to do. That's why I am looking for a way to write UIViews to PDFs or even bitmaps.

    I have tried the source code I copied from other Q/A within Stack Overflow. But it only gives me a blank PDF with the UIView bounds size.

    -(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
    {
        // Creates a mutable data object for updating with binary data, like a byte array
        NSMutableData *pdfData = [NSMutableData data];
    
        // Points the pdf converter to the mutable data object and to the UIView to be converted
        UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
        UIGraphicsBeginPDFPage();
    
        // draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
        [aView drawRect:aView.bounds];
    
        // remove PDF rendering context
        UIGraphicsEndPDFContext();
    
        // Retrieves the document directories from the iOS device
        NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
    
        NSString* documentDirectory = [documentDirectories objectAtIndex:0];
        NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
    
        // instructs the mutable data object to write its context to a file on disk
        [pdfData writeToFile:documentDirectoryFilename atomically:YES];
        NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
    }
    
  • Jason George
    Jason George over 12 years
    +1 I went through several posts on pdf generation before finding this simple solution.
  • iEamin
    iEamin over 12 years
    I wanted to do the same and your method seems to work fine but its quality is very low. Did I miss anything?
  • joshaidan
    joshaidan almost 12 years
    I suspect the quality is pretty low because it's taking the UIView and converting it to a raster, where as the other methods of rendering text and images directly preserves them as vectors in the PDF file.
  • Raj
    Raj over 11 years
    I am following this method , but I am getting a blank pdf generated . can any one help me ?
  • Rudi
    Rudi about 11 years
    It works awesome !!! cheers !! the only problem that I have is, it generate PDF in just one page. How can i Separate pages instead of having a long PDF file ?!
  • Alex Stone
    Alex Stone about 11 years
    Gives me a blank screen on iOS6.1
  • casillic
    casillic about 11 years
    This is the same exact code as my answer just broken out in two separate methods???? How do you think this fixed your blank screen problem when it is the same code??
  • Paresh Masani
    Paresh Masani almost 11 years
    Hi this works fine but it doesn't create pdf for whole uitableview. Can you please suggest how to take pdf for invisible cells as well?
  • Grant B
    Grant B almost 11 years
    Seems this works ok. Except if the view scrolls. In which case it only renders visible content. I imagine aview.bounds is controlling this ?
  • NashT
    NashT over 10 years
    I found that I was getting a blank screen when I ran the code on the view did load method. Maybe try and run the method when a button is pressed or something and it should be fine. The quality is fantastic thanks Casillic!
  • Daniel Arantes Loverde
    Daniel Arantes Loverde over 10 years
    iOS 5.1 get half page, is there normal ?
  • shim
    shim over 10 years
    This draws my view such that it fills the page. If I use drawInContext it's the right size but in the top left. How do I tell it where to draw the view?
  • amit soni
    amit soni over 10 years
    hey Casilic thats a great answer!! but i have some doubt like if i have a form which has 40-50 text field and 10-20 text view and user has entered 1000 words in text view then how can i show all text view data
  • casillic
    casillic over 10 years
    @amit soni -This method is really for onscreen info only. You probably should generate a report screen from the user's info and have that report converted to PDF. Best of luck.
  • Qing
    Qing over 9 years
    hey see this html2pdf cordova plugin github.com/moderna/cordova-plugin-html2pdf/blob/master/src/i‌​os/… it can print scrollable webview into multipage pdf
  • Jay Mayu
    Jay Mayu over 9 years
    this crashes in ios 8 at UIGraphicsEndPDFContext
  • Tom Tallak Solbu
    Tom Tallak Solbu about 9 years
    I had the same experience. Got a blank PDF from the first code. Splitting it in two as Alex did, made it work. Can't explain why.
  • David H
    David H about 8 years
    Your guard statement means the UIGraphicsEndPDFContext() isn't called - maybe add a defer earlier?
  • Denis Rumiantsev
    Denis Rumiantsev about 8 years
    @DavidH thanks, David, good idea! Also, I think, there's a good idea add a completion block kind completion: (success: Bool) -> () for guard return cases
  • David H
    David H about 8 years
    Yesterday I posted a Q&A on how to produce a high resolution image by rendering the view in a large image, then drawing the image into a PDF in interested: stackoverflow.com/a/35442187/1633251
  • Ahsan Ebrahim
    Ahsan Ebrahim about 8 years
    Can i somehow convert this in PNG or JPG ? because i want to be able to save it in camera roll as well as use in my application. Very nice solution though +1
  • Abha
    Abha almost 8 years
    @casillic your code works fine for UIView but I need for UIScrollview. I am using UIScrollview instead of UIView in parameter and using UIScrollview's ContentSize insted of UIView bound. This thing generate PDF but only for visible portion. You have any proper solution for UIScrollview ?
  • Saurabh Prajapati
    Saurabh Prajapati over 6 years
    this creating PDF for only firstPage! what about scrollview?
  • retrovius
    retrovius over 6 years
    Great question! I'm not the one to ask, however. Perhaps start another question?
  • Jonas Deichelmann
    Jonas Deichelmann over 6 years
    I've the same problem then @SaurabhPrajapati and I created an Question
  • Hussein
    Hussein about 5 years
    Thanks it worked, One question, So I have long scroll view, but the PDF file only shows a portion of it, so is there a way to tweak your code for example to give it Height ?
  • iAleksandr
    iAleksandr over 4 years
    @HusseinElbeheiry just use contentView to generate pdf. When I create a scrollView (UIScrollView) I will definitely create a contentView (UIView) and put the contentView in the scrollView, and I add all subsequent elements to the contentView. In this case, it is enough to use contentView to create a PDF document. contentView.exportAsPdfFromView
  • Midhun Narayan
    Midhun Narayan almost 3 years
    in iPhone where i can find this file, using files application it is not showing
  • Midhun Narayan
    Midhun Narayan almost 3 years
    Where i can find the saved PDF file?
  • Ashish Chauhan
    Ashish Chauhan almost 3 years
    @MidhunNarayan inside your app document directory. Just print the pdfPath on console and access it.
  • Midhun Narayan
    Midhun Narayan almost 3 years
    @AshishChauhan i have printed the path but when i open my file app it is not showing. do i need anything extra to see the converted pdf in my file app