I've been working on a simple test app to learn the ins and outs of the UIPageViewController. I have it working but I'm not convinced my execution is the best way. I hope some of you can point me in the right direction.
To get a basic understanding I used this tutorial as a starting point.
http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/
The tutorial creates an app that uses one viewController
for each of the pages presented by the UIPageViewController
. However I need to utilize the UIPageViewController to scroll thru pages that have completely different layouts. Therefore to take the tutorial a step further I created an master-detail application that uses the UIPageViewController in the detail view to display three different view controllers. I stuck with just displaying images and labels for this test app, but the app I am currently building has three viewControllers that will contain either a tableview, imageView and textViews, or some textFields.
Here is the storyboard for my test app.
I use the DetailViewController
as the data source for the PageViewController
. In viewDidLoad
of the DVC I establish the labels and images that will be used in the three content view controllers firstViewController
, secondViewController
, and thirdViewController
in this manner.
if ([[self.detailItem description] isEqualToString:@"F14's"]) {
//Here the page titles and images arrays are created
_pageTitles = @[@"Grim Reapers", @"Breakin the Barrier!", @"Top Gun"];
_pageImages = @[@"F14_Grim.jpg", @"F14boom.jpg", @"F14_topgun.jpg"];
//Here I call a method to instantiate the viewControllers
FirstController *selectedController = [self viewControllerAtIndex:0];
SecondController *nextController = [self viewControllerAtIndex:1];
ThirdController *lastController = [self viewControllerAtIndex:2];
[_vc addObject:selectedController];
[_vc addObject:nextController];
[_vc addObject:lastController];
_vc1 = @[selectedController];
} else if ([[self.detailItem description] isEqualToString:@"F35's"]){
//code is above is repeated
Below is the method to instantiate the viewControllers
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.pageTitles count] == 0) || (index >= [self.pageTitles count])) {
return nil;
}
// Create a new view controller and pass suitable data.
if (index == 0) {
FirstController *fvc = [self.storyboard instantiateViewControllerWithIdentifier:@"FirstPageController"];
fvc.imageFile = self.pageImages[index];
fvc.titleText = self.pageTitles[index];
fvc.pageIndex = index;
if ([_vc count]) {
//Here I have to replace the viewController each time it is recreated
[_vc replaceObjectAtIndex:0 withObject:fvc];
}
return fvc;
} else if (index == 1) {
//Code is repeated for remaining viewControllers
The code in viewDidLoad
is one area I feel I am doing unnecassary work. I don't believe I need to instantiate all three view controllers upon loading the DVC, but I didn't know how else to provide an array for the UIPageViewControllerDataSource protocol methods (viewControllerBeforeViewController
and viewControllerAfterViewController
).
Here is the viewControllerBefore..
method.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [_vc indexOfObject:viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
//notice here I call my instantiation method again essentially duplicating work I have already done!
return [self viewControllerAtIndex:index];
}
In summary it seems that I am unnecassarily recreating the view controllers with every swipe from from one page to another. Is this just how the pageViewController works or have I way over complicated the process. Any input would be great!
SOLUTION
Matt suggested an incredibly simple solution in using identifiers. In my Storyboard i simply checked the box that uses my already implemented Storyboard identifier as the Restoration Identifier
Then in viewDidLoad
rather than creating an array of viewControllers, simply create an array of strings that match the restoration identifiers.
if ([[self.detailItem description] isEqualToString:@"F14's"]) {
_pageTitles = @[@"Grim Reapers", @"Breakin the Barrier!", @"Top Gun"];
_pageImages = @[@"F14_Grim.jpg", @"F14boom.jpg", @"F14_topgun.jpg"];
FirstController *selectedController = [self viewControllerAtIndex:0];
[_vc addObject:@"FirstPageController"];
[_vc addObject:@"SecondPageController"];
[_vc addObject:@"ThirdPageController"];
_vc1 = @[selectedController];
Finally to determine the index in the delegate methods do this rather than what I was doing before:
NSString * ident = viewController.restorationIdentifier;
NSUInteger index = [_vc indexOfObject:ident];
It now works without having to unnecessarily instantiate the view controllers.
As a last note if anyone is using exactly what I have here you can get rid of the following snippet from the viewControllerAtIndex:
method.
if ([_vc count]) {
//Here I have to replace the viewController each time it is recreated
[_vc replaceObjectAtIndex:0 withObject:fvc];
}
See Question&Answers more detail:
os