An Example iOS 5 iPad UIPageViewController Application

From Techotopia
Jump to: navigation, search
PreviousTable of ContentsNext
Implementing a Page based iOS 5 iPad Application using UIPageViewControllerUsing the UIPickerView and UIDatePicker Components in iOS 5 iPad Applications


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


The previous chapter entitled Implementing a Page based iOS 5 iPad Application using UIPageViewController covered the theory behind implementing page curling view transitions using the UIPageViewController class. This chapter will work through the creation of an application designed to demonstrate this class in action.


Contents


The Xcode Page-based Application Template

When creating a new project within the Xcode environment, an option is provided to base the project on the Page-based Application template. When selected, this option generates a project containing an application designed to display a page for each month of the year. This is somewhat strange and something of an anomaly in that this is the only instance where Xcode provides a template that goes beyond providing a basic foundation on which to build and actually provides a sample application. Whilst this is useful for initial learning, unless an application with 12 pages labeled with months of the year is what you need, effort will be need to be invested removing existing functionality from the template before it can be used for other purposes.

Rather than use Xcode’s Page-based Application template, this chapter will work through the implementation of page based behavior using the Single View Application template as a starting point. The reasons for this are two-fold. Firstly, implementing UIPageViewController functionality without recourse to the page-based template provides the reader with a better understanding of how the implementation actually works. Secondly, it will typically be quicker to implement the UIPageViewController code by hand than to attempt to repurpose the example application provided by the Page-based Application template.

Creating the Project

Begin by launching Xcode and creating a new iOS Single View Application iPad project with a product name and class prefix of pageApp, making sure that the Use Storyboard option is not selected.


Adding the Content View Controller

The example application will use instances of a single view controller class to display pages to the user. The view will contain a UIWebView object onto which different HTML content will be displayed depending on the currently selected page. The view controller class will also need a data object property that will be used to hold the HTML content for the view.

To add the content view controller, select the Xcode File -> New -> New File… menu option and create a new Objective-C class. Configure the class to be a subclass of UIViewController with an XIB file included and name the class contentViewController. Select the contentViewController.h file and add an outlet to the web view component and a reference to the data object:

#import <UIKit/UIKit.h>

@interface contentViewController : UIViewController

@property (strong, nonatomic) IBOutlet UIWebView *webView;
@property (strong, nonatomic) id dataObject;
@end

Next, select the contentViewController.xib file and drag and drop a UIWebView component from the Object Library to the view canvas:

The user interface for an iOS 5 iPad UIPageViewController based app

Figure 26-1


Ctrl-click over the File’s Owner object, drag the connection line to the web view object and select the webView outlet from the resulting menu.

With the user interface designed, select the contentViewController.m file. Each time the user turns a page in the application, the data source methods for a UIPageViewController object are going to create a new instance of our contentViewController class and set the dataObject property to the HTML that is to be displayed on the web view object. As such, the viewWillAppear method of contentViewController needs to assign the value stored in the dataObject property to the web view object. To achieve this behavior, add the appropriate @synthesize directive to the file followed by the viewWillAppear method to assign the HTML to the web view:

#import "contentViewController.h"

@interface contentViewController ()

@end

@implementation contentViewController
@synthesize webView, dataObject;
.
.
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [webView loadHTMLString:dataObject 
       baseURL:[NSURL URLWithString:@""]];
}
.
.
@end

At this point work on the content view controller is complete. The next step is create the data model for the application.

Creating Data Model

The data model for the application is going to consist of an array object containing a number of string objects, each configured to contain slightly different HTML content. For the purposes of this example, the data source for the UIPageViewController instance will be the application’s pageAppViewController class. This class will, therefore, need references to an NSArray and a UIPageViewController object. It will also be necessary to declare this class as implementing the UIPageViewControllerDataSource protocol. Select the pageAppViewController.h file and add these references as follows together with an import directive for the contentViewController.h file:

#import <UIKit/UIKit.h>
#import “contentViewController.h”

@interface pageAppViewController : UIViewController
<UIPageViewControllerDataSource>

@property (strong, nonatomic) UIPageViewController *pageController;
@property (strong, nonatomic) NSArray *pageContent;
@end

The final step in creating the model is to add a method to the pageAppViewController.m file to add the HTML strings to the array and then call that method from viewDidLoad: (note also the addition of the @synthesize directive):

#import "pageAppViewController.h"

@interface pageAppViewController ()

@end

@implementation pageAppViewController
@synthesize pageController, pageContent;
.
.
- (void) createContentPages
{
    NSMutableArray *pageStrings = [[NSMutableArray alloc] init];
    for (int i = 1; i < 11; i++)
    {
        NSString *contentString = [[NSString alloc]
initWithFormat:@"<html><head></head><body><h1>Chapter %d</h1><p>This is the page %d of content displayed using UIPageViewController in iOS 5.</p></body></html>", i, i];
        [pageStrings addObject:contentString];
    }
    pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
.
.
- (void)viewDidLoad
{
    [super viewDidLoad];
    [self createContentPages];
}

The application now has a content view controller and a data model from which the content of each page will be extracted by the data source methods. The next logical step therefore is to implement those data source methods. As previously outlined in Implementing a Page based iOS 5 iPad Application using UIPageViewController, instances of the UIPageViewController class need a data source. This takes the form of two methods, one of which is required to return the view controller to be displayed after the currently displayed view controller, and the other the view controller to be displayed before the current view controller. Since the pageAppViewController is going to act as the data source for the page view controller object, these two methods, together with two convenience methods (which we will borrow from the Xcode Page-based Application template) will need to be added to the pageAppViewController.m file. Begin by adding the two convenience functions:

</pre>

  1. import "pageAppViewController.h"

@interface pageAppViewController ()

@end

@implementation pageAppViewController @synthesize pageController, pageContent;

- (contentViewController *)viewControllerAtIndex:(NSUInteger)index {

   // Return the data view controller for the given index.
   if (([self.pageContent count] == 0) || 
           (index >= [self.pageContent count])) {
       return nil;
   }
   // Create a new view controller and pass suitable data.
   contentViewController *dataViewController = 
       [[contentViewController alloc]
       initWithNibName:@"contentViewController" 
       bundle:nil];
   dataViewController.dataObject = 
     [self.pageContent objectAtIndex:index];
   return dataViewController;

}

- (NSUInteger)indexOfViewController:(contentViewController *)viewController {

    return [self.pageContent indexOfObject:viewController.dataObject];

} . . @end </pre>

The viewControllerAtIndex: method begins by checking to see if the page being requested is outside the bounds of available pages by checking if the index reference is zero (the user cannot page back beyond the first page) or greater than the number of items in the pageContent array. In the event that the index value is valid, a new instance of the contentViewController class is created and the dataObject property set to the contents of the corresponding item in the pageContent array of HTML strings.

The indexOfViewController method is passed a viewController and is expected to return the index value of the controller. It does this by extracting the dataObject property of the view controller and finding the index of the matching element in the pageContent array.

All that remains to be implemented as far as the data source is concerned are the two data source protocol methods which, in turn, make use of the two convenience methods to return the view controllers before and after the current view controller:

- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerBeforeViewController:
(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:
       (contentViewController *)viewController];
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }

    index--;
    return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:
        (contentViewController *)viewController];
    if (index == NSNotFound) {
        return nil;
    }

    index++;
    if (index == [self.pageContent count]) {
        return nil;
    }
    return [self viewControllerAtIndex:index];
}

With the data source implemented, the next step is to create and initialize an instance of the UIPageViewController class.

Running the UIPageViewController Application

Click on the Run button to compile and launch the application in the iOS iPad Simulator. Once loaded the first content page should appear. A right to left gesture motion on the screen will cause the page to transition to the second page of content and reversing the gesture direction will page backwards:


An iPad iOS 5 UIPageViewController application running

Figure 26-2

Running the UIPageViewController Application

Click on the Run button to compile and launch the application in the iOS iPad Simulator. Once loaded the first content page should appear. A right to left gesture motion on the screen will cause the page to transition to the second page of content and reversing the gesture direction will page backwards:


An iPad iOS 5 UIPageViewController application running

Figure 26-2

Summary

The UIPageViewController class was introduced in iOS 5 and provides a mechanism for implementing page turning navigation effects within applications. Within this chapter we have worked through an example designed to demonstrate some of the features of this new class in action.


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


PreviousTable of ContentsNext
Implementing a Page based iOS 5 iPad Application using UIPageViewControllerUsing the UIPickerView and UIDatePicker Components in iOS 5 iPad Applications