Working with raw PDF documents can be a time consuming and frustrating task. This tutorial will teach you how to use the VFR Library to quickly access, display, and browse PDF documents within your own applications!
Introduction
There are many scenarios where it may become necessary to display a PDF in an iOS App. In doing so, there can be many complications in getting PDFs to render correctly. There are also additional features such as printing, e-mailing, viewing bookmarks, handling rotation, and more that should be accounted for. While many of these features may not be complicated to include, some can be very tricky to get working correctly.
Step 1: Install The VFR Library
For this app, we'll start out by creating a new Xcode project as a "Single-View Application". Also, we'll be using ARC, so make sure that "Use Automatic Reference Counting" is checked.
Required Frameworks
Our PDF "Reader" library requires a few additional frameworks to be added to our project: QuartzCore, ImageIO, and MessageUI.
If you're already familiar with adding frameworks to a project, then you can skip this next part and get right into "Adding the Library to Our Project".
Otherwise, to add those frameworks, click on your Project from the Project Navigator. Choose your target, then find the "Build Phases" tab at the top. Underneath "Build Phases", expand the "Link Binary With Libraries" section and then click the "+" button in the lower left-hand corner.
Now, add the required frameworks (QuartzCore, ImageIO, MessageUI).
Your "Link Binary With Options" section should now look like this:
Adding the Library to Our Project
Our PDF "Reader" library can be downloaded from GitHub here: https://github.com/vfr/Reader. We can now drag our "Reader" library into our project and we're almost ready to start. I've created a new Group in my project called "PDF Reader" where I'm adding the library (you only need to include the "Sources" and "Graphics" directories).
Unfortunately, at this point, "Reader" is not ARC-ready. So, we'll have to do some refactoring. There are three (3) files that we'll need to manually update before we can tell Xcode to refactor for ARC: "ReaderDocument.m", "ReaderContentPage.m", and "ReaderThumbFetch.m" (for reference, you can find basic instructions for these files here: http://www.vfr.org/2012/05/reader-arc-conversion/.)
Basically, what we're going to do is edit each of these files to use plain __bridge casts, like so:
ReaderDocument.m:
Change
... = [NSString stringWithString:(id)theString];
to ...
= [NSString stringWithString:(__bridge id)theString];
ReaderContentPage.m:
Change
... = CGPDFDocumentCreateX((CFURLRef)fileURL, phrase);
to...
... = CGPDFDocumentCreateX((__bridge CFURLRef)fileURL, phrase);
ReaderThumbFetch.m:
Change
... = CGImageSourceCreateWithURL((CFURLRef)thumbURL,
to...
... = CGImageSourceCreateWithURL((__bridge CFURLRef)thumbURL, NULL);
With the above changes made, go to Edit > Refactor > Convert to Objective-C ARC...
You will be prompted to "Select Targets to Convert." Make sure that your current target is selected, then press "Check".
The prompt will guide you through the process, you should be able to just hit "Next" without issue.
Then you'll be able to "Review Changes", and you can select "Save".
Finally, we need to include our "Reader" library header into our main ViewController.h and set our main view controller as a ReaderViewControllerDelegate
:
#import "ReaderViewController.h"
@interface MTViewController : UIViewController <ReaderViewControllerDelegate>
Step 2: Setup
The complicated part of our project setup is over. From this point, it's quick work to start viewing PDFs in our apps -complete with printing, bookmarks, thumbnails, email, and more!
First thing, let's add a demo PDF to our project. Once again, I've created a new group, this one I've called "PDFs".
I've then added a PDF named "typo_tips.pdf" that I downloaded from fontshop.com in the "resources directory" that you can use (direct link). Of course, feel free to try any PDF that you already have as well.
To open our PDF, we'll add a single UIButton in our main ViewController.xib.
We'll then hook up our UIButton to a method that we'll call didClickOpenPDF
.
The rest of the work will be handled within this "didClickOpenPDF" method in our main ViewController.m.
First, we'll grab our PDF document from our app bundle.
NSString *file = [[NSBundle mainBundle] pathForResource:@"typo_tips" ofType:@"pdf"];
Next, we'll create a "ReaderDocument" that our "Reader" will be able to view. Notice that, if our document were password protected, we could set the password as the final parameter.
ReaderDocument *document = [ReaderDocument withDocumentFilePath:file password:nil];
Next, we make sure that our "ReaderDocument" was successfully created, before implementing our "ReaderViewController" which will handle the rest of the PDF operations for us.
if (document != nil) { }
And between the braces in the "document" check, we'll load our "ReaderViewController" object.
if (document != nil) { ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document]; readerViewController.delegate = self; }
In this example, we'll be presenting our "ReaderViewController" as a modal view, although we could alternatively present it with a UINavigationController if our app were using a UINavigationController. We have various options for the style of presentation and transition that we could use for our ModalViewController, so I'd encourage you to experiment with the styles that work best for your app. In this case, we'll try to imitate the native Apple PDF style from iBooks.
Add the following lines after "readerViewController.delegate = self;".
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
Lastly, we need to actually present our ModalViewController:
[self presentModalViewController:readerViewController animated:YES];
As you can see, once we've included our "Reader" library, it's relatively simple to view and interact with PDFs in our iOS apps. Here's the entire method that we used:
- (IBAction)didClickOpenPDF:(UIButton *)sender { NSString *file = [[NSBundle mainBundle] pathForResource:@"typo_tips" ofType:@"pdf"]; ReaderDocument *document = [ReaderDocument withDocumentFilePath:file password:nil]; if (document != nil) { ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document]; readerViewController.delegate = self; readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; readerViewController.modalPresentationStyle = UIModalPresentationFullScreen; [self presentModalViewController:readerViewController animated:YES]; } }
If you run and build our application now, you'll see the many features already working for your PDF viewer.
There's just one final thing we need to handle: dismissing our PDF Reader ViewController when the user presses the "Done" button.
This is handled with a simple, straightforward method:
- (void)dismissReaderViewController:(ReaderViewController *)viewController { [self dismissModalViewControllerAnimated:YES]; }
Step 3: Options
As mentioned earlier, the "Reader" library supports many of these functions beyond just showing PDFs, such as printing, bookmarks, etc. By default, most of these features are enabled. You can, however, choose to set these each as best suits your application by editing the "ReaderConstants.h" file. Each of these features is listed on the "Reader" GitHub page, which I'll show here:
- READER_BOOKMARKS - If TRUE, enables page bookmark support.
- READER_ENABLE_MAIL - If TRUE, an email button is added to the toolbar
(if the device is properly configured for email support). - READER_ENABLE_PRINT - If TRUE, a print button is added to the toolbar
(if printing is supported and available on the device). - READER_ENABLE_THUMBS - If TRUE, a thumbs button is added to the toolbar
(enabling page thumbnail document navigation). - READER_DISABLE_IDLE - If TRUE, the iOS idle timer is disabled while
viewing a document (beware of battery drain). - READER_SHOW_SHADOWS - If TRUE, a shadow is shown around each page
and the page content is inset by a couple of extra points. - READER_STANDALONE - If FALSE, a "Done" button is added to the toolbar
and the -dismissReaderViewController: delegate method is messaged when
it is tapped. - READER_DISABLE_RETINA - If TRUE, sets the CATiledLayer contentScale
to 1.0f. This effectively disables retina support and results in
non-retina device rendering speeds on retina display devices at
the loss of retina display quality.
Next Time: Writing PDF Documents
Now you've seen how easy it can be to view PDFs in iOS. In the next tutorial in this series, we'll look at some of the ways to create our own PDFs within our Apps.
Comments