Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using UIPageViewController with swift and multiple view controllers

I'm trying to utilize a UIPageViewController in a Swift app with multiple view controllers. I have 2 view controllers on my storyboard (firstViewController and secondViewController) that are both embedded in their own navigation controllers. They have storyboard identifiers "FirstViewController" and "SecondViewController". I also have a page view controller on my storyboard with identifierPageView controller and set the navigation to horizontal and transition style to scroll in the attributes inspector. FirstViewController is the root view controller of the app

I want firstViewController to be the first page of the page view controller so I have written its class like so:

import Foundation
import UIKit

class FirsttViewController: UITableViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate  {

    override func viewDidLoad() {

        let pageViewController: PageViewController = self.storyboard.instantiateViewControllerWithIdentifier("PageViewController") as PageViewController

        pageViewController.delegate = self

        var viewControllers: [UIViewController] = [self]

        pageViewController.setViewControllers(viewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
        pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)

        self.addChildViewController(pageViewController)
        self.view.addSubview(pageViewController.view)
        pageViewController.didMoveToParentViewController(self)


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


        let secondViewController: SecondViewController = self.storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController


        return secondViewController
    }

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

        return nil
    }

    func presentationCountForPageViewController(pageViewController: UIPageViewController!) -> Int {
        return 2
    }

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

the file for SecondViewController looks like this:

import Foundation
import UIKit

class SecondViewController: UITableViewController, UIPageViewControllerDataSource {

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

        return nil
    }

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

        let firstViewController: FirstViewController = self.storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as FirstViewController
        return firstViewController
    }

}

However, when I run my app it doesn't seem to have a page view controller. It just shows firstViewController and that's it. No dots, no pages, etc. Anyone know whats wrong? I've found a couple of Objective-C tutorials but they all cover using one view controller for all the pages and have a root view controller that is not a page in the pageView so they weren't too much help. I was hoping it was going to be like using a tabBarController where you just click and drag to the view controllers but thats not the case unfortunately. like I said I've not worked with one of these before so I'm sort of lost.

update:

following iiFreeman's suggestion, I subclassed UIPageViewController and made it the root in my storyboard file. below is the subclass

import Foundation
import UIKit
class PageViewController: UIPageViewController {

    override func viewDidLoad() {

        let startingViewController = self.storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as FirstViewController

        self.dataSource = startingViewController
        var viewControllers: [UIViewController] = [startingViewController]

        self.setViewControllers(viewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)



    }

}

However now the app just hangs on a black screen and eventually Xcode loses connection to the simulator. I'm not really sure what's up.

like image 554
Stone Preston Avatar asked Aug 19 '14 15:08

Stone Preston


People also ask

How do I present a two view controller in Swift?

For solving this, you will need to do a pretty simple trick which is to take a screenshot from the first view controller and passing it to the second view controller to display it while presenting the third view controller. You can check this repository to see how it is exactly could be done (Swift 3).

How do I use page view controller in storyboard?

Name the class as PageContentViewController and create it a subclass of UIViewController. Navigate to the next UI in the same Storyboard. Choose the page content view controller and set the custom class to PageContentViewController below the Identify Inspector. Then, we build outlets for the image view and label.

How does UIPageViewController work?

When navigating from page to page, the page view controller uses the transition that you specify to animate the change. In tvOS, the UIPageViewController class provides only a way to swipe between full-screen content pages. Unlike in iOS, a user cannot interact with or move focus between items on each page.

What is pageview controller in iOS?

The PageViewController is used in many of the iOS applications to let the user navigate between the various pages of the content of the application. The navigation can be controlled programmatically in the application. The PageViewController uses the transition that we specify to animate the change.


1 Answers

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

like image 176
Stone Preston Avatar answered Sep 17 '22 12:09

Stone Preston