In this tutorial, we will create a PDF reader that allows users to select a PDF file from their device and display it in the app.
We will first show a basic approach to display a PDF file, and then enhance our implementation by using the UIDocumentPickerViewController for a more user-friendly experience. To display the PDF file, we’ll make use of the PDFView class from the PDFKit framework.
PDFKit is a framework available in iOS that allows you to display and manipulate PDF documents. Introduced in iOS 11, PDFKit provides a range of functionalities, including rendering, searching, selecting, and modifying PDF content.
PDFKit is built on top of the Quartz 2D graphics engine, and its key components include the following:
Open the Object Library by clicking on the “+” button in the top-right corner of Xcode.
Search for “View” and drag it to your ViewController’s scene.
Resize the new view to fill the ViewController’s scene.
With the new view selected, go to the Identity Inspector on the right panel.
In the Class field, type PDFView and hit Enter. This will set the custom class of the view to PDFView.
Now, in the ViewController.swift file, at the top of the file add:
import PDFKit
Code language: Swift (swift)
Place the storyboard and Swift file side by side, then drag the PDFView from the storyboard to the Swift file to create an IBOutlet, and name it pdfView.
Next, locate the PDF file you want to display in your app (in this example, we’ll use fw9.pdf).
Drag and drop the PDF file into your app’s bundle in the Project Navigator within Xcode.
A dialog box will appear; make sure “Copy items if needed” is checked and press “Finish.”
After completing these steps, your PDF file will be included in your project, and your Project Navigator should display the imported file as shown below:
Now, you can display it like this:
import PDFKit import UIKit class ViewController: UIViewController < @IBOutlet weak var pdfView: PDFView! override func viewDidLoad() < super.viewDidLoad() displayPDF() > func displayPDF() < guard let path = Bundle.main.path(forResource: "fw9", ofType: "pdf") else < return > let url = URL(fileURLWithPath: path) guard let pdfDocument = PDFDocument(url: url) else < return > pdfView.document = pdfDocument // Enable automatic scaling of the PDF view to fit the content within the available space pdfView.autoScales = true > >
Code language: Swift (swift)
Now let’s move on to a more user-friendly approach by allowing users to pick a PDF file from their device using UIDocumentPickerViewController .
Make your view controller conform to UIDocumentPickerDelegate :
class ViewController: UIViewController, UIDocumentPickerDelegate
Code language: Swift (swift)
In the Main.storyboard , add a Navigation Item to the top-right corner of the view controller’s navigation bar (If you don’t have Navigation Controller, select the View Controller and then go Editor > Embed In > Navigation Controller) and create an @IBAction connection named openDocumentPickerAction .
class ViewController: UIViewController, UIDocumentPickerDelegate< @IBOutlet weak var pdfView: PDFView! override func viewDidLoad() < super.viewDidLoad() // displayPDF() > // . @IBAction func openDocumentPickerAction(_ sender: UIBarButtonItem) < // . > >
Code language: Swift (swift)
Now, create a new method with a name showDocumentPicker , and add the following code:
private func showDocumentPicker() < let documentPicker: UIDocumentPickerViewController let pdfType = "com.adobe.pdf" if #available(iOS 14.0, *) < let documentTypes = UTType.types(tag: "pdf", tagClass: UTTagClass.filenameExtension, conformingTo: nil) documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: documentTypes) > else < documentPicker = UIDocumentPickerViewController(documentTypes: [pdfType], in: .import) > documentPicker.delegate = self present(documentPicker, animated: true, completion: nil) >
Code language: Swift (swift)
This method creates a UIDocumentPickerViewController instance, allowing the user to select PDF files from their device. It configures the document picker to support both iOS 14+ and earlier versions, setting the delegate to the view controller and presenting the picker.
Also, import the UniformTypeIdentifiers at the top of the file to be able to use UTType
import PDFKit import UniformTypeIdentifiers
Code language: Swift (swift)
Next, implement the documentPicker(_:didPickDocumentsAt:) and documentPickerWasCancelled(_:) delegate methods in your view controller:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) < guard let url = urls.first else < return > displayPDF(url: url) > func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) < dismiss(animated: true, completion: nil) >
Code language: Swift (swift)
These methods handle the user’s selection of a PDF file or the cancellation of the document picker, respectively.
Update the displayPDF() method to accept a URL as a parameter:
private func displayPDF(url: URL) < guard let pdfDocument = PDFDocument(url: url) else < print("Failed to load the PDF document.") return > pdfView.document = pdfDocument // Enable automatic scaling of the PDF view to fit the content within the available space pdfView.autoScales = true >
Code language: Swift (swift)
This method takes a URL as input and attempts to create a PDFDocument object. If successful, it assigns the document to the pdfView , enabling automatic scaling to fit the content within the available space.
Finally, call the showDocumentPicker() method from the IBAction we created before:
class ViewController: UIViewController, UIDocumentPickerDelegate< @IBOutlet weak var pdfView: PDFView! override func viewDidLoad() < super.viewDidLoad() // displayPDF() > // . @IBAction func openDocumentPickerAction(_ sender: UIBarButtonItem) < showDocumentPicker() >>
Code language: Swift (swift)
This is only the basic setup of PDFKit, which simply shows a PDF. However, in many cases, we want additional features, such as navigating to the next page, displaying the PDF horizontally instead of vertically, or downloading the file.
Navigate smoothly between pages in a PDF document using the code below:
// Go to the previous or next page pdfView.goToPreviousPage(nil) pdfView.goToNextPage(nil)
Code language: Swift (swift)
Access a specific page within a PDF document directly with this code snippet:
// Jump to a specific page if let page = pdfView.document?.page(at: pageIndex) Code language: Swift (swift)
Adjust the zoom level within a document using the following code:
// Zoom in and out pdfView.scaleFactor += 0.1 // Zoom in pdfView.scaleFactor -= 0.1 // Zoom out
Code language: Swift (swift)
Easily scroll through a document either vertically or horizontally using the following code:
pdfView.autoScales = true pdfView.displayDirection = .vertical // Vertical scrolling pdfView.displayDirection = .horizontal // Horizontal scrolling
Code language: Swift (swift)
Change the display mode of a document with the code below:
pdfView.displayMode = .singlePage // Single page pdfView.displayMode = .singlePageContinuous // Continuous scrolling pdfView.displayMode = .twoUp // Double page
Code language: Swift (swift)
Modify the background color of the PDF view using this code snippet:
pdfView.backgroundColor = .lightGray
Control the display of page breaks with the following code:
pdfView.pageBreakMargins = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) pdfView.displaysPageBreaks = true // Enable pdfView.displaysPageBreaks = false // Disable
Code language: Swift (swift)
Toggle the visibility of page thumbnails using this code:
pdfView.displaysAsBook = true // Show pdfView.displaysAsBook = false // Hide
Code language: Swift (swift)
Create and add an annotation to the current page with the code snippet below:
if let page = pdfView.currentPage < let annotationBounds = CGRect(x: 100, y: 100, width: 200, height: 50) let annotation = PDFAnnotation(bounds: annotationBounds, forType: .highlight, withProperties: nil) annotation.color = .yellow page.addAnnotation(annotation) >
Code language: Swift (swift)
Remove annotations from the current page using the following code:
if let page = pdfView.currentPage < for annotation in page.annotations < if let markupAnnotation = annotation as? PDFAnnotationMarkup, markupAnnotation.markupType == .highlight < page.removeAnnotation(annotation) > > >
Code language: Kotlin (kotlin)
Retrieve and print the selected text using this code snippet:
if let selectedText = pdfView.currentSelection?.attributedString < print("Selected text: \(selectedText.string)") >
Code language: Swift (swift)
Find occurrences of specific text within a document using the code below:
if let document = pdfView.document < let searchText = "example" let selections = document.findString(searchText, withOptions: .caseInsensitive) for selection in selections < print("Found '\(searchText)' on page \(selection.pages.first!.label!)") > >
Code language: Swift (swift)
Retrieve and print the text content of the current page with this code snippet:
if let page = pdfView.currentPage, let content = page.string < print("Page content: \(content)") >
Code language: Swift (swift)
Generate a new PDF document using the following code:
// Define the page size let pageSize = CGSize(width: 612, height: 792) // Create a UIGraphicsPDFRenderer with the page size let renderer = UIGraphicsPDFRenderer(bounds: CGRect(origin: .zero, size: pageSize)) // Render the PDF data let pdfData = renderer.pdfData < (context) in // Begin a new PDF page context.beginPage() // Draw content on the page let text = "Hi!" let font = UIFont.systemFont(ofSize: 18) let attributes: [NSAttributedString.Key: Any] = [.font: font] (text as NSString).draw(at: CGPoint(x: 100, y: 100), withAttributes: attributes) > // Create a new PDFDocument with the PDF data let newDocument = PDFDocument(data: pdfData) // Save the new document let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("newDocument.pdf") newDocument?.write(to: documentURL)
Code language: Swift (swift)
Rotate the current page using this code:
if let page = pdfView.currentPage < page.rotation += 90 // Rotate clockwise >
Code language: Swift (swift)
Secure a PDF document with encryption using the following code snippet:
let password = "secret_password" // Get the raw data of the document guard let rawData = pdfView.document?.dataRepresentation() else < print("Failed to get the raw data of the document") return > // Create a new PDF document with the raw data let encryptedDocument = PDFDocument(data: rawData) // Set the encryption properties let options: [PDFDocumentWriteOption: Any] = [ .userPasswordOption: password, .ownerPasswordOption: password ] // Save the encrypted document if let encryptedDocument = encryptedDocument < let encryptedDocumentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("encryptedDocument.pdf") encryptedDocument.write(to: encryptedDocumentURL, withOptions: options) >
Code language: Swift (swift)
Unlock a password-protected PDF document using the code below:
let unlockPassword = "secret_password" if pdfView.document?.unlock(withPassword: unlockPassword) == true < print("Document unlocked successfully!") > else < print("Wrong password. Failed to unlock the document.") >
Code language: Swift (swift)
If you have any questions, please feel free to leave a comment below