Changes

Jump to: navigation, search

An Example iOS 7 UIPageViewController Application

9,210 bytes added, 20:47, 14 April 2014
Creating the Data Model
NSString *contentString = [[NSString alloc]
initWithFormat:@"<html><head></head><body><br><h1>Chapter %d</h1><p>This is the page %d of content displayed using UIPageViewController in iOS 7.</p></body></html>", i, i];
[pageStrings addObject:contentString];
}
_pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
.
.
- (void)viewDidLoad
{
[super viewDidLoad];
[self createContentPages];
}
</pre>
<google>BUY_IOS7</google>
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 7 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>
#import "PageAppViewController.h"
 
@interface PageAppViewController ()
 
@end
 
@implementation PageAppViewController
 
 
- (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] init];
*/
 
UIStoryboard *storyboard =
[UIStoryboard storyboardWithName:@"Main"
bundle:[NSBundle mainBundle]];
 
ContentViewController *dataViewController =
[storyboard
instantiateViewControllerWithIdentifier:@"contentView"];
 
dataViewController.dataObject = _pageContent[index];
return dataViewController;
}
 
- (NSUInteger)indexOfViewController:(ContentViewController *)viewController
{
return [_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.
 
Since the view controller is stored in the storyboard file, the following code is used to get a reference to the storyboard and to create a new ContentViewController instance:
 
<pre>
UIStoryboard *storyboard =
[UIStoryboard storyboardWithName:@"MainStoryboard"
bundle:[NSBundle mainBundle]];
 
ContentViewController *dataViewController =
[storyboard
instantiateViewControllerWithIdentifier:@"contentView"];
</pre>
 
The indexOfViewController method is passed a viewController object 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:
 
<pre>
- (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];
}
</pre>
 
With the data source implemented, the next step is to create and initialize an instance of the UIPageViewController class.
 
== Initializing the UIPageViewController ==
 
All that remains is to create the UIPageViewController instance and initialize it appropriately. Since this needs to be performed only once per application invocation a suitable location for this code is the viewDidLoad method of the PageAppViewController class. Select the PageAppViewController.m file and modify the viewDidLoad method so that it reads as follows:
 
<pre>
- (void)viewDidLoad
{
[super viewDidLoad];
[self createContentPages];
NSDictionary *options = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin]
forKey: UIPageViewControllerOptionSpineLocationKey];
 
_pageController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options: options];
 
_pageController.dataSource = self;
[[_pageController view] setFrame:[[self view] bounds]];
 
ContentViewController *initialViewController =
[self viewControllerAtIndex:0];
NSArray *viewControllers =
[NSArray arrayWithObject:initialViewController];
 
[_pageController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
 
[self addChildViewController:_pageController];
[[self view] addSubview:[_pageController view]];
[_pageController didMoveToParentViewController:self];
}
</pre>
 
All the code for the application is now complete. Before compiling and running the application some time needs to be taken to deconstruct and analyze the code in the viewDidLoad method.
After constructing the data model with the call to the createContentPage method an NSDictionary object is created to contain the options that will be applied to the page controller object. In this instance the only option used is to set the spine location to appear on the left of the screen:
 
<pre>
NSDictionary *options =
[NSDictionary dictionaryWithObject:
[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin]
forKey: UIPageViewControllerOptionSpineLocationKey];
</pre>
 
Next, an instance of the UIPageViewController class is created using the previously created options object and specifying horizontal navigation orientation:
 
<pre>
_pageController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options: options];
</pre>
 
Since the current class is going to act as the data source for the page controller this also needs to be configured. We also want the pages to fill the entire screen, so need to set the bounds appropriately:
 
<pre>
_pageController.dataSource = self;
[[_pageController view] setFrame:[[self view] bounds]];
</pre>
 
Before the first page can be displayed a view controller must first be created. This can be achieved by calling our viewControllerAtIndex: convenience method. Once a content view controller has been returned it needs to be assigned to an array object:
 
<pre>
ContentViewController *initialViewController =
[self viewControllerAtIndex:0];
NSArray *viewControllers =
[NSArray arrayWithObject:initialViewController];
</pre>
 
Note that only one content view controller is needed because the page controller is configured to display only one, single sided page at a time. Had the page controller been configured for two pages (with a mid location spine) or for double sided pages it would have been necessary to create two content view controllers at this point and assign both to the array.
With an array containing the content view controller ready, the array needs to be assigned to the view controller with the navigation direction set to forward mode:
 
<pre>
[_pageController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
</pre>
 
Finally, the standard steps need to be taken to add the page view controller to the current view:
 
<pre>
[self addChildViewController:_pageController];
[[self view] addSubview:[_pageController view]];
[_pageController didMoveToParentViewController:self];
</pre>
 
<h1>Chapter %d</h1><p>This is the page %d of content displayed using UIPageViewController in iOS 7.</p></body></html>", i, i];
[pageStrings addObject:contentString];
}

Navigation menu