Viewing PDF and iWork Documents in iOS

Viewing PDF’s and iWork documents in iOS is very simple (If you only have to view the document). So simple in fact that I actually do not have to write about it ;). So you can rather consider this a place to get a snippet of code for viewing documents in your application.

There are more than one ways you can do this based on your requirement.

1. Using a UIWebView

UIWebView can be used to load documents as well along with loading URL’s. It is especially useful if you have to load different files/urls in your application and the user needs to only view the documents.
Opening a local PDF file in UIWebView can be done by instantiating a UIWebView, adding it to your view as a subview and loading a request with the local file path as the URL.

        // Create a webview instance and add it to your view.
        let webview = UIWebView()
        webview.frame = self.view.bounds
        self.view.addSubview(webview)
        // loacate a file and create a URL request with the file URL and load it to the webview
        if let fileURL = NSBundle.mainBundle().pathForResource("SamplePDF", ofType: "pdf") { // Use if let to unwrap to fileURL variable if file exists
            let theFileURL = NSURL(fileURLWithPath: fileURL)
            webview.loadRequest(NSURLRequest(URL:theFileURL!))
        }

And.. that’s it! Better yet, you can open any iWork document files or even redirect to an external URL using this method.

2. Using QLPreviewController of Quick Look Framework

QLPreviewController provides the UI for displaying the documents as well as automatically provides action menu with print/share options. It even provides UI to display documents as a list if there is more than one document and automatically handles the UI for previewing the selected document.

For using the QLPreviewController we will first have to import the QuickLook framework as below.

import QuickLook

Make your controller correspond to the QLPreview Datasource and Delegate protocols

class ReaderViewController: UIViewController, QLPreviewControllerDataSource, QLPreviewControllerDelegate

Similar to a UITableView, it enables a controller to register as the datasource to provide the required data. QLPreviewControllerDataSource has two required methods that needs to be implemented as described below. It also has a delegate protocol for view presentation callbacks which is optional.

    //  Simple method to wrap the presenting of QLPreviewController
    func loadDocUsingQLPreview() {
        let previewController = QLPreviewController()
        previewController.dataSource = self
        self.presentViewController(previewController, animated: true, completion: nil)
    }
    
    // QLPreviewControllerDataSource required methods
    func numberOfPreviewItemsInPreviewController(controller: QLPreviewController!) -> Int {
        return 1
    }
    
    func previewController(controller: QLPreviewController!, previewItemAtIndex index: Int) -> QLPreviewItem! {

        if let fileURL = NSBundle.mainBundle().pathForResource("SamplePDF", ofType: "pdf") { // Use if let to unwrap to fileURL variable if file exists
            return NSURL(fileURLWithPath: fileURL)
        }
        return nil;
    }

The two required datasource methods are:

  • numberOfPreviewItemsInPreviewController: to specify the number of items to be previewed.
  • previewController: previewItemAtIndex: to provide a QLPreviewItem for the selected index. Since this is a category of NSURL, you can directly return an NSURL as mentioned in the documentation (see below). It also allows customisation to provide a Title along with URL for which you will have to create your custom object.

The methods in this protocol are also declared as a category on the NSURL class. As a result, you can use NSURL objects directly as preview items—provided that you want to use the default titles of those items. A default title is the last path component of an item’s URL. If you want to supply your own preview item titles, create your own preview item objects that adopt this protocol.
– Apple: QLPreviewItem Protocol Reference for iOS: QLPreviewItem

Advantages using QLPreviewController over UIWebView

  • Handles Presentation and loading of documents.
  • Delegate methods to notify on view controller presentation calls.
  • Nicely displays multiple documents as preview and allows user to manually select and view documents.
  • Automatically provides a navigation bar with a Done and an Action button to share/print the document and automatically animates and hides while reading.
  • More control over the preview process than UIDocumentInteractionController class.
Multiple File Selection Option
Multiple File Selection Option
File Action Menu
Document Action Menu
Document Preview Screen
Document Preview Screen

3. Using UIDocumentInteractionController

When your app needs to interact with files it cannot preview or open on its own, use a UIDocumentInteractionController object to manage those interactions.
Apple: Document Interaction Programming Topics for iOS: Previewing and Opening Files

For displaying documents, UIDocumentInteractionController also uses QLPreviewController so the preview UI is same as that in our previous implementation.

Reasons for using UIDocumentInteractionController:

  • Previewing the document along with automatic handling of all interactions related to file preview and menu display.
  • Provides option to print/share/open the document using another application without having to preview/if cannot be previewed.
  • Delegate methods for view transitions and to track user interactions with menu items.

For previewing documents using UIDocumentInteractionController, our view controller should correspond to the UIDocumentInteractionControllerDelegate protocol and implement the documentInteractionControllerViewControllerForPreview: method and return the presenting controller for the document interaction controller.

class ReaderViewController: UIViewController, UIDocumentInteractionControllerDelegate
    func loadDocUsingDocInteractionController() {

        if let fileURL = NSBundle.mainBundle().pathForResource("SamplePDF", ofType: "pdf") { // Use if let to unwrap to fileURL variable if file exists
            let docInteractionController = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: fileURL)!)
            docInteractionController.delegate = self
            docInteractionController.presentPreviewAnimated(true)
        }
    }
    
    // Return the view controller from which the UIDocumentInteractionController will present itself.
    func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
        return self
    }

The UIDocumentInteractionController has the below methods for various cases:

  • presentPreviewAnimated: for presenting the preview controller.
  • dismissPreviewAnimated: to programatically dismiss the preview.
  • presentOptionsMenuFromBarButtonItem:animated: to provide an action sheet with options to share/print etc.
  • presentOpenInMenuFromBarButtonItem:animated: to provide an action sheet with options to open the file in a different application which supports the document type.

4. Alternate PDF viewers

There are few commercial libraries also available but one of the best third party libraries remain to be the VFR Reader. Also it is free and open source 🙂

You can check the project here:

That’s all there is to it. Thanks for reading! 🙂

References

  1. Apple: UIWebView Class Reference
  2. Apple: QLPreviewController Class Reference
  3. Apple: UIDocumentInteractionController Class Reference
  4. Apple: PDF Document Creation, Viewing, and Transforming

2 thoughts on “Viewing PDF and iWork Documents in iOS

  1. Hi Vijay, thanks for this tutorial. Do you think that the output from using UIWebView and QLPreviewController of Quick Look Framework are the same? I am referring only to the rendering output.

    Liked by 1 person

    1. Hi William! Thanks for writing.
      At a high level, I can say It is the same. I also think it is safe to say the difference between the output rendered using UIWebView and QLPreviewController is negligible (Just because I have not encountered any problems dealing with the limited content I have worked with :)) although there is a little difference between these two while rendering/presenting based on the content.
      However, if there is any specific scenario/reason that you are asking this, I would appreciate a little more detail on this.
      Thanks again!

      Like

Leave a comment