Using Uipageviewcontroller with Swift and Multiple View Controllers

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 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).

SwiftUI + UIPageViewController, multiple view types

You can wrap your pages in AnyView:

PageView(pages: [
AnyView(Color.red),
AnyView(Color.blue),
AnyView(Text("Hi"))
])

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).

UIPageViewController with different ViewControllers, right way?

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!

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

Swift and Page view controller between 2 views

This explains how to create a paged view controller using storyboard:

  1. Drag a UIPageViewController into storyboard and mark it as the initial view controller.
  2. Then create a new UIPageViewController subclass - say call it TutorialPageViewController and assign it to the object you just dragged into storyboard.
  3. Now drag in two new UIViewControllers into storyboard and create/assign a subclass for each, as you would do normally.
  4. Give each of these two view controllers a Storyboard ID e.g RedViewController & GreenViewController.
  5. Now add this code in your TutorialPageViewController which creates
    and shows the correct view controller when you swipe:

TutorialPageViewController:

class TutorialPageViewController: UIPageViewController, UIPageViewControllerDataSource {
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newColoredViewController("RedViewController"),
self.newColoredViewController("GreenViewController")]
}()

private func newColoredViewController(color: String) -> UIViewController {

return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewControllerWithIdentifier("\(color)ViewController")
}

override func viewDidLoad() {
super.viewDidLoad()

dataSource = self

if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
}

func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}

let previousIndex = viewControllerIndex - 1

guard previousIndex >= 0 else {
return nil
}

guard orderedViewControllers.count > previousIndex else {
return nil
}

return orderedViewControllers[previousIndex]
}

func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}

let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count

guard orderedViewControllersCount != nextIndex else {
return nil
}

guard orderedViewControllersCount > nextIndex else {
return nil
}

return orderedViewControllers[nextIndex]
}
}

See the article for more details and a tutorial on this.

https://spin.atomicobject.com/2015/12/23/swift-uipageviewcontroller-tutorial/

Update: Page Dots

Read from point 7 on this part 2 article that shows how to add a UIPageControl and change the dots on swipe:

https://spin.atomicobject.com/2016/02/11/move-uipageviewcontroller-dots/

@IBOutlet weak var pageControl: UIPageControl!

func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
didUpdatePageCount count: Int) {
pageControl.numberOfPages = count
}

func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
didUpdatePageIndex index: Int) {
pageControl.currentPage = index
}


Related Topics



Leave a reply



Submit