How to Implement Uipageviewcontroller That Utilizes Multiple Viewcontrollers

How to implement UIPageViewController that utilizes multiple ViewControllers

First of all, you are absolutely right that the view controllers that constitute the "pages" of the UIPageViewController can be completely different in nature. Nothing whatever says that they have to be instances of the same view controller class.

Now let's get to the actual problem, which is that you very sensibly need a way to provide the next or previous view controller given the current view controller. That is, indeed, the main issue when using a page view controller.

It would not really be terrible to hold an array of view controllers. After all, a view controller is a lightweight object (it is the view that is the heavyweight object). However, you are also right that the way you're handling this seems clumsy.

My suggestion is: if you are going to hold the view controller instances in a storyboard, then why not just keep an array of their identifiers? Now you've got an array of three strings. How simple can you get? You will also need a single instance variable that keeps track of which identifier corresponds to the view controller that having its view used as the current page (so that you can work out which one is "next" or "previous"); this could just be an integer indexing into the array.

There is then absolutely nothing wrong with instantiating a view controller each time the user "turns the page". That is what you are supposed to do when a view controller is needed. And you can readily do this by identifier.

Finally, note that if you use the scroll style of page view controller, you won't even have to do that, because the page view controller caches the view controllers and stops calling the delegate methods (or, at least, calls them less).

How do I add multiple ViewControllers to my UIPageViewController?

Here is the fully working code if anyone is interested. Took me a while to get it right, but eventually this worked perfectly!

#import "PageViewController.h"

@interface PageViewController ()

@property (nonatomic, retain) UIViewController *first;
@property (nonatomic, retain) UIViewController *second;
@property (nonatomic, retain) UIViewController *third;
@property (nonatomic, retain) UIViewController *fourth;
@property (nonatomic, retain) UIViewController *fifth;

@end

@implementation PageViewController {
NSArray *viewControllers;
}

- (UIViewController *)first {
if (!_first) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Secondary" bundle:nil];
_first = [sb instantiateViewControllerWithIdentifier:@"1"];
}
return _first;
}

- (UIViewController *)second {
if (!_second) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Secondary" bundle:nil];
_second = [sb instantiateViewControllerWithIdentifier:@"2"];
}
return _second;
}
- (UIViewController *)third {
if (!_third) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Secondary" bundle:nil];
_third = [sb instantiateViewControllerWithIdentifier:@"3"];
}
return _third;
}
- (UIViewController *)fourth {
if (!_fourth) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Secondary" bundle:nil];
_fourth = [sb instantiateViewControllerWithIdentifier:@"4"];
}
return _fourth;
}
- (UIViewController *)fifth {
if (!_fifth) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Secondary" bundle:nil];
_fifth = [sb instantiateViewControllerWithIdentifier:@"5"];
}
return _fifth;
}


- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = self;

// Aggancio il view controller iniziale.
[self setViewControllers:@[self.first]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {

UIViewController *nextViewController = nil;

if (viewController == self.first) {
nextViewController = self.second;
}
if (viewController == self.second) {
nextViewController = self.third;
}
if (viewController == self.third) {
nextViewController = self.fourth;
}
if (viewController == self.fourth) {
nextViewController = self.fifth;
}

return nextViewController;
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {

UIViewController *prevViewController = nil;

if (viewController == self.fifth) {
prevViewController = self.fourth;
}
if (viewController == self.fourth) {
prevViewController = self.third;
}
if (viewController == self.third) {
prevViewController = self.second;
}
if (viewController == self.second) {
prevViewController = self.first;
}

return prevViewController;
}

@end

IOS - How to create a UIPageViewController with multiple ViewController from different XIB files

You need to create like a main view controller and in this view controller you initialise all VC you need.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
// initialize controllers
self.controllers = [[NSMutableArray alloc]
initWithObjects:
[[Document1ViewController alloc] initWithNibName:@"Document1ViewController" bundle:nil],
[[Document2ViewController alloc] initWithNibName:@"Document2ViewController" bundle:nil],
[[Document3ViewController alloc] initWithNibName:@"Document3ViewController" bundle:nil],
nil];
}
return self;
}

Then you need to create a scrollview with pagination with the number of VC you want.

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

for (NSUInteger i =0; i < [self.controllers count]; i++) {
[self loadScrollViewWithPage:i];
}

self.pageControl.currentPage = 0;
_page = 0;
[self.pageControl setNumberOfPages:[self.controllers count]];

UIViewController *viewController = [self.controllers objectAtIndex:self.pageControl.currentPage];
if (viewController.view.superview != nil) {
[viewController viewWillAppear:animated];
}

self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [self.controllers count], scrollView.frame.size.height);
}

When you scroll you need to change the VC order in the scrollView and is possible because you have a array with the references of all VC's.

You can see one example here

Using UIPageViewController with swift and multiple view controllers

OK, I got it figured out. since I needed the page view controller to be the root and handle everything, I subclassed UIPageViewControlleras iiFreeman suggested and conformed to the delegate and data source protocols. I then set up the controller in viewDidLoad, and implemented the delegate and data source methods. I added a property store an array of storyboard identifiers as well as one to keep track of the current index of that array. I also added a helper method to return the correct view controller given an index. I didn't need anything in my other tableView controller subclasses since my page view controller was handling everything. One thing I did realize is since my tableViewControllers were embedded in navigation controllers, my page view controller actually needed to show the navigation controllers, not the tableView controllers. below is my implementation:

import Foundation
import UIKit

class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

var index = 0
var identifiers: NSArray = ["FirstNavigationController", "SecondNavigationController"]
override func viewDidLoad() {

self.dataSource = self
self.delegate = self

let startingViewController = self.viewControllerAtIndex(self.index)
let viewControllers: NSArray = [startingViewController]
self.setViewControllers(viewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)



}

func viewControllerAtIndex(index: Int) -> UINavigationController! {

//first view controller = firstViewControllers navigation controller
if index == 0 {

return self.storyboard.instantiateViewControllerWithIdentifier("FirstNavigationController") as UINavigationController

}

//second view controller = secondViewController's navigation controller
if index == 1 {

return self.storyboard.instantiateViewControllerWithIdentifier("SecondNavigationController") as UINavigationController
}

return nil
}
func pageViewController(pageViewController: UIPageViewController!, viewControllerAfterViewController viewController: UIViewController!) -> UIViewController! {

let identifier = viewController.restorationIdentifier
let index = self.identifiers.indexOfObject(identifier)

//if the index is the end of the array, return nil since we dont want a view controller after the last one
if index == identifiers.count - 1 {

return nil
}

//increment the index to get the viewController after the current index
self.index = self.index + 1
return self.viewControllerAtIndex(self.index)

}

func pageViewController(pageViewController: UIPageViewController!, viewControllerBeforeViewController viewController: UIViewController!) -> UIViewController! {

let identifier = viewController.restorationIdentifier
let index = self.identifiers.indexOfObject(identifier)

//if the index is 0, return nil since we dont want a view controller before the first one
if index == 0 {

return nil
}

//decrement the index to get the viewController before the current one
self.index = self.index - 1
return self.viewControllerAtIndex(self.index)

}


func presentationCountForPageViewController(pageViewController: UIPageViewController!) -> Int {
return self.identifiers.count
}

func presentationIndexForPageViewController(pageViewController: UIPageViewController!) -> Int {
return 0
}

}

this post helped me out a lot

How can I have multiple viewcontrollers in UIPageViewController display at once

I don't think you can do what you are describing. A page view controller shows one page, or 2 pages with a spine in the middle. It has pages "waiting in the wings" for when the user flips/slides new pages, but I don't think it supports showing pages off the edge.

That said, it wouldn't be that hard to create your own parent/child view controller arrangement that does what you want. you might even be able to base it off of a UICollectionView.

UIPageViewController and different view controllers

I use this template to achieve this.
It comes from a (possible duplicate) question with two interesting answers that you can find there.

EDIT:

TL;DR the idea is to use storyboard identifiers for your different view controllers to instantiate them when needed, using the PageViewControllerDataSource protocol methods.

Swipe between multiple view controllers using UIPageViewController

The crash occurs while trying to access the restorationIdentifier of your view controller. You used ! to unwrap it but it's nil ; as a first solution, set the restorationIdentifier in the storyboard.

Sample Image

In general, use ! to unwrap a value only if you're sure it's not nil (by adding a if statement just before).



Related Topics



Leave a reply



Submit