Using View Controller from storyboard programmatically in a Page View Controller - ios

I have an app with a storyboard. I am using several View Controller with segues to choose from a table view depending on the item selected. I want to have a page view controller in which the pages will be one or more view controller from the storyboard, even repeating them. I am trying to init the Page View Controller like this:
....
self.dataSource=self;
UIViewController *initialViewController =[self viewControllerAtIndex:current];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationOrientationHorizontal animated:NO completion:nil];
....
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index
{
if ((descriptions.count == 0) ||
(index >= descriptions.count)) {
return nil;
}
current=index;
// Create a new view controller and pass suitable data.
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
Description *description=[DBCompany getDescriptionById:language descriptionId:[[descriptions objectAtIndex:index] integerValue]];
UIViewController *viewController=nil;
if(description.frame==100){
viewController=[[Company100ViewController alloc] init];
((Company100ViewController*)viewController).companyId = companyId;
}
return viewController;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ((descriptions.count == 0) ||
(current-1 < 0)) {
return nil;
}
return [self viewControllerAtIndex:current-1];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ((descriptions.count == 0) ||
(current >= descriptions.count)) {
return nil;
}
return [self viewControllerAtIndex:current+1];
}
However the viewcontroller appears in black. I think is because I am adding the UIviewcontroller class but not connecting it to any view, XIB, in my case with the storyboard.
How can I use the different view controller from the storyboard programmatically to use in a page view controller?

If you do viewController=[[Company100ViewController alloc] init] then yes this not a controller that is associated to a storyboard or an XIB. To load the controller with an XIB you have to do the following:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
Company100ViewController * vc = (Company100ViewController *)[sb instantiateViewControllerWithIdentifier:#"vc-identifier"];
In the storyboard make sure you set the view controller's id to vc-identifier; whatever identifier you choose.

Swift 3
let sb = UIStoryboard(name: "MainStoryboard", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "vc-identifier")

Swift
In case you want to init from a UIViewController object.
guard let vc = storyboard?.instantiateViewController(withIdentifier: "nameVC") else {
return
}
// add to current view
view.addSubview(vc.view)

Related

UISplitViewController with nav stacks in both master and detail - how to remove a VC from one of the stacks?

I am debugging legacy code which is always fun. The old code tried to mock the splitView delegate methods, causing all sorts of issues - mainly crashing: on a Plus device in Portrait, rotating to landscape caused the crash - if there was no detail view set, old code attempted to create one in a dodgy hack and it was just useless...
My app is UISplitViewController based, where I have a navigation stack in both master and detail sides of the splitView.
By reading though SO and using this example and was able to implement UISplitViewController delegate methods and everything is working correctly in regards to rotation, and showing the correct master/detail views when appropriate. Here is my implementation: (apologies for wall of code snippets)
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
if ([secondaryViewController isKindOfClass:[UINavigationController class]]
&& [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[AECourseHTMLTableViewController class]]
&& ([(AECourseHTMLTableViewController *)[(UINavigationController *)secondaryViewController topViewController] htmlContentEntry] == nil)) {
// If the detail controller doesn't have an item, display the primary view controller instead
return YES;
}
return NO;
}
And the other splitView delegate method - see comments in code for where I'm stuck.
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController {
// If detail view already exists
if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
if ([controller isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)controller visibleViewController] isKindOfClass:[AECourseHTMLTableViewController class]]) {
return controller;
}
}
}
// Create detail view
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"CourseHTMLNav"];
if ([navController.viewControllers.firstObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
AECourseHTMLTableViewController *courseViewController = navController.viewControllers.firstObject;
[self configureViewController:courseViewController entry:self.contentSection.sections[0] indexPath:courseViewController.currentIndexPath];
}
// Enable back button
UIViewController *controller = [navController visibleViewController];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
if (!self.splitViewController.isCollapsed) {
UINavigationController *navController = self.splitViewController.viewControllers.firstObject;
AEContentMenuTableViewController *contentMenuVC = navController.viewControllers.firstObject; // This controller needs to be master in Landscape
NSMutableArray<UIViewController *> *controllers = [navController.viewControllers mutableCopy]; // Contains 3 controllers, first needs removed
NSMutableArray *toDelete = [NSMutableArray new];
for (UIViewController *viewController in controllers)
if ([viewController isKindOfClass:[contentMenuVC class]] || [viewController isKindOfClass:[AECourseHTMLTableViewController class]]) {
[toDelete addObject:viewController]; // Remove first VC, so master should become AEContentMenuVC?
break;
}
// Remove the object
[controllers removeObjectsInArray:toDelete];
// Set viewControllers
navController.viewControllers = controllers;
}
return navController;
}
AECourseHTMLTableViewController has next/prev buttons to select the next row in the tableview of the tableview menu class class (AEContentMenuTableViewController). I have a delegate function which can tell me the current indexPath in which AECourseHTML... is using from AEContentMenu..., and when calling it, it selects the menu tableview row and instantiates a new AECourseHTML... and pushes it.
This is where I'm stuck. In Portrait, pressing next/prev is fine, it selects the correct row and works as expected. But once I rotate the device, both master and detail views show the detail view. I can press "Back" on the master view, and it takes me to the correct AEContentMenu... class. As noted in the code snippet comments, I need to remove a ViewController from the master stack (the first object actually), and AEContentMenu... should become the first object of that stack - so when rotating, that should be the master view.
Apologies for such a long post, I've been banging my head with this for weeks now and I want to include as much info as possible in this question. Thanks in advance.
I found a solution which works well for my use cases. It may not be the cleanest code, but I'm happy with what I've got.
splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
remains unchanged. I have updated my splitViewController:separateSecondaryViewControllerFromPrimaryViewController: delegate method with the solution. Any feedback is welcome.
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController {
// If detail view already exists
if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
if ([controller isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)controller visibleViewController] isKindOfClass:[AECourseHTMLTableViewController class]]) {
return controller;
}
}
}
// Return CourseVC
UINavigationController *navController = splitViewController.viewControllers.firstObject;
UIViewController *viewController;
for (viewController in navController.viewControllers) {
if ([navController.viewControllers.lastObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
return viewController;
} else {
// Create detail view
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"CourseHTMLNav"];
if ([navController.viewControllers.firstObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
// Enable back button
UIViewController *controller = [navController visibleViewController];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
AECourseHTMLTableViewController *courseViewController = navController.viewControllers.firstObject;
// If next/prev has been tapped, configure current ContentHTML
if (self.currentContentHTML) {
[self configureViewController:courseViewController entry:self.currentContentHTML indexPath:courseViewController.currentIndexPath];
} else {
// Create new ContentHTML from first row of AEContentMenuVC
[self configureViewController:courseViewController entry:self.contentSection.sections[0] indexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
return navController;
}
}
}
return navController;
}
Your top if statement should return nil. Since you were returning the nested navigation controller you were missing out on the default behaviour of popping the master navigation's top controller which is required so it can then be placed on the right.
The default behaviour will find that nested nav controller and pop it. However the reason you still need to search for it yourself is if it isn't there then you need to load the detail nav from the storyboard as you have done.

Getting correct frame size for UITableView(Controller) within UISplitViewController

I am using a UISplitViewController, where the Master View has two levels of hierarchy. Each node in the Master View has its own Detail View Controller, which I get from the storyboard like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *vc = [segue destinationViewController];
if ([vc isKindOfClass:[MKTableViewController class]]) {
MKTableViewController *mkVC = (MKTableViewController*) vc;
mkVC.mkController = self.mkController;
if ([vc isMemberOfClass:[ForumTableViewController class]]) {
mkVC.detailViewController = [self switchToDetailViewControllerWithIdentifier:#"DiscussionTableViewController"];
}
if ([vc isMemberOfClass:[UserTableViewController class]]) {
mkVC.detailViewController = [self switchToDetailViewControllerWithIdentifier:#"UserViewController"];
}
if ([vc isMemberOfClass:[GroupTableViewController class]]) {
mkVC.detailViewController = [self switchToDetailViewControllerWithIdentifier:#"GroupViewController"];
}
}
}
- (UIViewController*) switchToDetailViewControllerWithIdentifier:(NSString*)identifier {
UIStoryboard *storyBoard;
storyBoard = [UIStoryboard storyboardWithName:#"Main_iPad" bundle:nil];
UIViewController* vc = [storyBoard instantiateViewControllerWithIdentifier:identifier];
UINavigationController *navCon = self.splitViewController.viewControllers[1];
UIViewController* detailViewController = navCon.viewControllers[0];
for (UIViewController *child in detailViewController.childViewControllers) {
[child willMoveToParentViewController:nil];
[child.view removeFromSuperview];
[child removeFromParentViewController];
}
[detailViewController addChildViewController:vc];
[detailViewController.view addSubview:vc.view];
[vc didMoveToParentViewController:detailViewController];
return vc;
}
This all works, but the frame settings do not match. If I print the frame sizes of the different views, I get this:
Size of SplitViewController.view: X:768.0 Y:1024.0
MasterNavigationViewController.view: X:320.0 Y:768.0
Size of DetailNavigationViewController: X:703.0 Y:768.0
Size of DiscussionViewController: X:768.0 Y:1024.0
If I manually set the size of the DiscussionViewController.view using the following statement, it looks OK, but somehow I have to arbitrarily subtract 20 from the height. This feels wrong... What would be the correct way of doing this? The size of the DiscussionViewController is set to 'inferred', is there a way to have the view.frame automatically set correct when moving it to the Detail Navigation Controller?
frame.size.height = detailNavController.view.frame.size.height - detailNavController.navigationBar.frame.size.height - 20.0;
(The Detail Navigation Bar Controller has a Navigation Bar and no Toolbar)
Version Information:
iPad App for iOS7
Xcode 5.1.1

How to release memory of child view controllers

I have a page view controller in which i have four view controller.My question is when i go to second view controller i need to release memory of first,third and fourth view controller similarly for other three view controller.How to find whether it is allocating or deallocating memory for view controllers using instruments.I have tried using dealloc but the method is not getting called and i am using arc.I have my code as follows
-(NewsViewController *)news
{
if (!news) {
UIStoryboard *storyboard = self.storyboard;
news = [storyboard instantiateViewControllerWithIdentifier:#"News"];
}
return news;
}
-(PriceViewController *)price
{
if (!price) {
UIStoryboard *storyboard = self.storyboard;
price = [storyboard instantiateViewControllerWithIdentifier:#"price"];
}
return price;
}
- (CommentryViewController *)commentry {
if (!commentry) {
UIStoryboard *storyboard = self.storyboard;
commentry = [storyboard instantiateViewControllerWithIdentifier:#"commentry"];
}
return commentry;
}
-(WatchlistViewController *)watchlist {
if (!watchlist) {
UIStoryboard *storyboard = self.storyboard;
watchlist = [storyboard instantiateViewControllerWithIdentifier:#"watchlist"];
}
return watchlist;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
UIViewController *nextViewController = nil;
if (viewController == self.news) {
nextViewController = self.commentry;
}
else if(viewController == self.commentry) {
titleView.text = #"Commentry";
[titleView sizeToFit];
//self.title=#"Commentry";
nextViewController = self.price;
}
else if(viewController == self.price) {
titleView.text = #"Price";
[titleView sizeToFit];
nextViewController = self.watchlist;
}
else if(viewController == self.watchlist) {
titleView.text = #"WatchList";
[titleView sizeToFit];
}
return nextViewController;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
UIViewController *prevViewController = nil;
if (viewController==self.watchlist) {
titleView.text = #"WatchList";
prevViewController = self.price;
}
else if (viewController == self.price) {
titleView.text = #"Price";
prevViewController = self.commentry;
}
else if (viewController == self.commentry) {
titleView.text = #"Commentry";
prevViewController = self.news;
}
else if(viewController == self.news) {
titleView.text = #"News";
}
return prevViewController;
}
In a UIPageViewController if you call -viewControllers, you should see all the view controllers loaded in memory and if this array is filled with some you should not force a dealloc of them, because page view controller manages the memory pretty well removing unneeded view controllers from that array. That means that if no one else has ownership on them the will be dealloced and you should see a a decrease in the Memory Allocation tool.
View controllers has their own way to handle thier memory occupation(rules are always the same of the language, but there is an inner mechanism that helps to keep memory low) when in memory pressure, that is because view controllers do not occupy much memory by themselves, but the resources loaded with them do.
Memory management behavior in VC has changed a lot since iOS 6 (and I'm going to talk only about that), when the system is in need of memory, not visible VCs mark their views backing layer as dirty ready to be released, usually this frees up a lot of memory, but if in you view controller you create and take the ownership on a lot of objects, this memory can never be freed, unless you do in the method -didReceiveMemoryWarning, or you get rid of them when you have finished using them.
You should never call dealloc and in ARC is also forbidden, if you have a huge memory grow in memory, you need to check how you manage the memory of those objects.
I make an example to be clear, let's suppose that in you page VC you load other VCs with full screen images, if your load them using +imageNamed those images will finish in a cache and even if your VC is correctly dealloced the image occupation will remain.

UIPageViewController to Force reload ChildView controller

I have a slider in Root UIPageViewController which need to pass the slider value to the child View Controller. Which works for other child view controller except than the one already loaded !
I have Slider in RootView for Slider Value Change I have called -
- (void)sliderValueChanged:(UISlider *)slider {
fontValue = slider.value;
DetailsVC *dataViewController = [self viewControllerAtIndex:self.newsIndex storyboard:self.storyboard];
[dataViewController refreshViewForFontSize:slider.value];
}
I have usual Methods of UIPageViewController which works fine. Just the problem is it works only after the ViewDid Load method is called in child ViewControllers
- (DetailsVC *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard {
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
return nil;
}
DetailsVC *dataViewController = [[DetailsVC alloc] init];
dataViewController.fontValue = fontValue;
return dataViewController;
}
Finally found the solution
DetailsVC *dataViewController = [self viewControllerAtIndex:currentIndex-1 storyboard:self.storyboard];
dataViewController.textColor = textColor;
NSArray *viewControllers = #[dataViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
The currect way to get reload the child viewcontroller is to get it via
setViewControllers: direction: animated: completion:
If anyone need a example project I have upload it at
https://github.com/bishalg/BGPageViewController

UIPageViewController with different ViewControllers, right way?

Basically, I have a Table View Controller and a normal View Controller, I want to make it so that I can swipe between the view controllers like on the home screen of app (obvious same type of view controller).
But the thing I am finding is that the UIpageviewcontroller is usaully used when you have multiple views of the same type of viewcontroller for example swiping between pictures, you could just set an index in the class and an array of images relating to the index and set it up that way.
But I'm trying to get different view controllers in this set up. Right now it's two but will definitely go up to about 4.
Is UIPageViewController the right way to set it up or something else.
EDIT: Code added below
//
// ViewController.m
// testb
//
#import "MainPostController.h"
#import "MainTableViewController.h"
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize pageViewController;
#synthesize indexValue = _indexValue;
- (void)viewDidLoad
{
[super viewDidLoad];
self.indexValue= 1;
NSLog(#"main intitated");
// Do any additional setup after loading the view, typically from a nib.
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
MainTableViewController *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainTableViewController"];
MainPostController *pvc;
NSArray *viewcontrollers =[NSArray arrayWithObjects:tvc, pvc, nil];
[self.pageViewController setViewControllers:viewcontrollers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 30);
[self addChildViewController:pageViewController];
[self.view addSubview:pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if (self.indexValue == 1) {
MainPostController *pvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainPostController"];
self.indexValue = 0 ;
return pvc;
}
else return nil;
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if (self.indexValue == 0) {
MainTableViewController *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainTableViewController"];
self.indexValue = 1;
return tvc;
}
else return nil;
}
#end
I want the intial view to be the table view controller(tvc) and i want the Post view controller(pvc) to be to its left. I can load it up and swipe between the two easily but sometime i can swipe further than the boundaries so basically if i start on the tvc-->pvc-->tvc then i can go further by swiping right to the same tvc then the boundary is reached or go the other way tvc-->pvc-->tvc-->pvc-->pvc by swiping left for the last transition. The boundaries are reached in the end. Once the app intially launches at tvc i cannot go forward a page, (which is what should happen) and if i go tvc-->pvc and try to go to a page to the left it wont let me (which is what should happen)
Thanks
Finally done it, didn't have to add any properties to the different classes. Just tested the class. Heres the code:
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[MainTableViewController class]]) {
MainPostController *pvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainPostController"];
return pvc;
}
else return nil;
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[MainPostController class]]) {
MainTableViewController *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainTableViewController"];
return tvc;
}
else return nil;
}
Should be easy to add more view controllers as i just need to add else if statements to test them. The main problem solver was the method isKindOfClass:
Thanks for your reply guys!
I had the same issue, here's what i did and working perfectly.
.h
#interface ViewController : UIViewController <UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageViewController;
#property (nonatomic) NSArray *viewControllerIDArr;
#end
.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupDefaults];
}
- (void)setupDefaults
{
self.viewControllerIDArr = #[#"FirstViewController", #"SecondViewController"];
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
UIViewController *startingViewController = [self.storyboard instantiateViewControllerWithIdentifier:self.viewControllerIDArr[0]];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 45);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
- (UIViewController *)getViewControllerFromClass:(Class)class isNext:(BOOL)next
{
NSInteger index = [self.viewControllerIDArr indexOfObject:[NSString stringWithFormat:#"%#", class]];
if (next)
index+=1;
else
index-=1;
if (index < 0 || index >= self.viewControllerIDArr.count)
return nil;
else
return [self.storyboard instantiateViewControllerWithIdentifier:self.viewControllerIDArr[index]];
}
#pragma mark - Page View Controller Data Source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
return [self getViewControllerFromClass:viewController.class isNext:NO];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
return [self getViewControllerFromClass:viewController.class isNext:YES];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
Note:
self.viewControllerIDArr containers Storyboard ID's of the viewControllers.
It looks like this in storyboard:
This is an old question but since I bumped into this problem I'll post my solution.
The first time I did this I added my code
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { }
Which received problems when you continuously scroll the views using two fingers and didFinishAnimating is not called unless scrolling stops.
So I just used this delegate method instead.
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
let currentView = pendingViewControllers[0]
if currentView is UINavigationController && currentView.restorationIdentifier == "Settings" {
index = 0
} else if currentView is UINavigationController && currentView.restorationIdentifier == "Main" {
index = 1
} else if currentView is UINavigationController && currentView.restorationIdentifier == "Message" {
index = 2
}
}
Where currentView is the currentUIViewController (or UINavigationController like what I am using, using a restorationIdentifier to identify my navigation controller as well), and index is a global value to know which UIViewController in the array to instantiate.
You could use a UIViewPageViewController, with some custom logic.
As you say, the normal way to handle this is to have the page view controller's data source simply create the same type of view controller for each page and fill it with data from your model.
In your case, you could add a pageNumber property to all the view controllers that you're going to host in your page view controller. Then, in the data source methods, use the page number of the old view controller to figure out the new page number. Then have a switch statement based on page number. Each case in the switch statement would create a different type of view controller.

Resources