Building an iPad Reader for War of the Worlds

This tutorial will use the Leaves open-source project to build a simple iPad reader for The War of the Worlds by H.G. Wells. Along the way, we'll take a quick look at the code powering the Leaves project, discuss implementation details, and explore a few alternative options for achieving a similar effect.

About the Leaves Project

Leaves is an open-source iOS component that simulates a page turning transition between content views, much like the transition found in Apple's official iBooks application. The project was originally written by Tom Brow and made available on GitHub, where it was later forked by Ole Begemann to add support for multi-page layouts.

To see the Leaves project in action, take a look at the following video demo of the project this tutorial will teach you to create:

Of course, the page curling effect demonstrated above isn't unique to either this demo or the iBooks application. Other native iOS applications (such as the Maps app) also use a similar effect. For the full backstory on exactly how Apple achieves this in their applications and why iOS SDK developers need to resort to a component like Leaves, check out "App Store-Safe Page Curl Animations" by Ole Begemann and "Apple's iBooks Dynamic Page Curl" by Steven Troughton-Smith.

We'll come back to some of the theory behind the Leaves project later, but let's go ahead and jump into creating our War of the Worlds reader to get a feel for how the code looks in action.

Building the WOTW PDF Reader

Step 1: Download the Project Resources

The download file attached to this Mobiletuts+ post contains the Leaves source code and several public domain resources (including the War of the Worlds text) used in the project.

Step 2: Create a New Xcode Project

Open Xcode and create a new project using the "View-based Application" template. Name the project "WOTW" and select "iPad" from the device family drop-down.

Step 3: Add the Project Resources to Xcode

Drag-and-drop the following files into the "Supporting Files" group in Xcode:

WOTW.pdf

Icon.png

Icon@2x.png

All of the above files can be found in the "Resources" folder of the download attached to this tutorial.

Next, add the actual Leaves component code. Create a new group called "Leaves" under the "WOTW" folder in the Xcode project navigator, and then drag-and-drop the following files into the Leaves group:

Utilities.h

Utilities.m

LeavesCache.h

LeavesCache.m

LeavesView.h

LeavesView.m

LeavesViewController.h

LeavesViewController.m

NOTE: Be sure to select the "Copy items into destination group's folder" checkbox when adding the above files.

Step 4: Link the QuartzCore Framework

The animation created by Leaves is dependent on the QuartzCore Framework. Consequently, you'll need to link this framework against your project in order for Leaves to work. To do this in Xcode 4, start by selecting "WOTW" in the Project Navigator. Next, select the "WOTW" target and then the "Build Phases" tab. Then expand the "Link Binary With Libraries" drop down and click the plus symbol to add a new framework. Finally, select "QuartzCore" from the list of available frameworks and click "Add". The result of this process is visually displayed below:

Step 5: Subclass LeavesViewController

For our project, we want the reader to launch immediately into the War of the Worlds text without a lander page. To do this, we'll need to make the WOTWViewController class inherit from the LeavesViewController class instead of directly from UIViewController. Do this by opening WOTWViewController.h and modifying the interface declaration to the following:

@interface WOTWViewController : LeavesViewController {
}

Line 1 above modifies WOTWViewController to inherit from LeavesViewController instead of directly from UIViewController. Because LeavesViewController itself inherits from UIViewController, you can think of the WOTWViewController as the grandchild of UIViewController

So, what do we get for our trouble? LeavesViewController conforms to the Leaves data source and delegate protocols (discussed later) and defines custom -loadView and -viewDidLoad implementations that add a special kind of UIView called a LeavesView to the current view hierarchy. The LeavesView class is responsible for most of the work behind the page curl animation.

Step 6: Add Necessary Import Statements

In order for the WOTWViewController class to be able to inherit from the LeavesViewController class, we need to import the LeavesViewController code. Add the following two #import lines to WOTWViewController.h:

#import "Utilities.h"
#import "LeavesViewController.h"

Wondering what that Utilities.h import statement is for? As we'll see later, the Leaves project relies on a clever function declared in that file called aspectFit. Many Cocoa-Touch projects maintain a Utilities class to declare and define helper code used throughout the application.

Step 7: Declare the WOTW Data Members

For now, we'll only need to declare a single data member for our project. In the WOTWViewController.h file, add the following line of code:

CGPDFDocumentRef bookPDF;

We will use "bookPDF" as a reference to the WOTW.pdf file later. The CGPDFDocumentRef variable type will be used extensively in this tutorial and is worth further exploration in the official Apple documentation.

Step 8: Initialize Leaves

When the WOTWViewController is created we need to initialize the bookPDF data member declared above. As demonstrated in the Leaves sample project, you can do this with the following lines of code:

However, in our project we want to unarchive the WOTWViewController from our Interface Builder NIB, so the custom -(id)init function we just implemented will never be called. Instead, we need to provide a custom implementation for the -(id)initWithCoder: method in order to plug into the NIB unarchive process and perform custom initialization. It's good to leave the init in place so you have the option of creating this view controller manually, but we should create a new method for PDF initialization and call that method from both -(id)init and -(id)initWithCoder:.

Add the following line of code to the WOTWViewController.h file:

-(void)loadPDF;

Next, switch to WOTWViewController.m and implement the method as follows:

When our custom view controller is unarchived, the -(id)initWithCoder: method will be called and will then call the loadPDF method.

There's just one more thing. Having allocated memory for the bookPDF reference, we should also release that memory when we're done with it. We can do this by adding the following line of code to the -(void)dealloc: method:

CGPDFDocumentRelease(bookPDF);

Step 9: Implement the Leaves DataSource

Recall from Step 5 that the LeavesViewController class automatically adds an instance of the LeavesView class to the view hierarchy. The LeavesView class somehow needs to find out what content should be displayed in the view, and this is achieved by calling two custom data source methods: -(NSUInteger)numberOfPagesInLeavesView: and -(void)renderPageAtIndex:.

To provide a custom implementation for these, open WOTWViewController.m and add the following lines of code:

The -(NSUInteger)numberOfPagesInLeavesView: method simply indicates how many pages of content the LeavesViewController is responsible for displaying. If you were generating the content for Leaves manually or if you knew exactly how many pages were in the PDF, you could return that number manually here. Of course, it is always better to determine that number dynamically, and that is exactly what the CGPDFDocumentGetNumberOfPages() function does.

The -(void)renderPageAtIndex:inContext: method is responsible for actually drawing a CGContextRef after first adding the PDF content into the context passed in as ctx.

Testing the Code

Build and run the project. If everything goes well, you should now be able to see the War of the Worlds book cover and be able to flip through the pages with the page curl animation!

Leave a comment below if you'd like to see a tutorial on one of the above projects!

Do You Want to See More?

We've made some good progress in this tutorial. We can now interact with the War of the Worlds PDF and the page curling animation adds a great aesthetic feel to the eBook. However, there is still a lot of work to be done before this app would be ready for release on the App Store. For example, it would be great if the user was automatically returned to the last page they accessed when the app launches and it would also be really nice to have a UISlider or similar interface component to quickly skip between sections of the book. Other useful features might include search, text highlighting, a table of contents, or bookmarks.

I want to make sure my iOS SDK tutorials cover topics that the Mobiletuts+ community is interested in. Do you think I should build out a full-fledged eReader and share my source code? Or perhaps you'd like to see a tutorial on something completely different? Answer the following poll and let me know!

UPDATE 9/7/2011: The poll is now closed. Over 60% of respondents expressed an interest in seeing more posts on adding a Table of Contents and/or a UISlider and a tutorial using a UISlider has been published (Link Below).

You can also send your feedback to my Twitter (@markhammonds) account, though I admit I normally only use Twitter when not working on freelance projects or writing tutorials, so you may just want to contact me via e-mail instead.