slider
slider

Reputation: 2816

Duplicate view controllers when using nav controllers inside page view controller

Courtesy of Using UIPageViewController with swift and multiple view controllers, I'm embedding navigation controllers in a UIPageViewController so I can horizontally scroll thru them (like swipe nav). Problem:

When I reach the first or last nav controller, and then swipe in the opposite direction, the 2nd-to-first/last nav controller will duplicate. So I'll have 5 nav controllers. For example:

Starting at FirstNav, swipe left all the way to FourthNav. When I swipe right through the array of controllers from FourthNav, the sequence will be: ThirdNav, ThirdNav, SecondNav, FirstNav. Can anyone find out what's going on?

 class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

        var index = 0
        var identifiers: NSArray = ["FirstNav", "SecondNav", "ThirdNav", "FourthNav"]

        override func viewDidLoad() {
            super.viewDidLoad()

            self.dataSource = self
            self.delegate = self

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

        }

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

            if index == 0 {

                return self.storyboard!.instantiateViewControllerWithIdentifier("FirstNav") as! UINavigationController
            }


            if index == 1 {

               return self.storyboard!.instantiateViewControllerWithIdentifier("SecondNav") as! UINavigationController

            }

           if index == 2 {

               return self.storyboard!.instantiateViewControllerWithIdentifier("ThirdNav") as! UINavigationController
           }

           if index == 3 {

               return self.storyboard!.instantiateViewControllerWithIdentifier("FourthNav") 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 0
    }

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


}

Upvotes: 1

Views: 1584

Answers (1)

ndmeiri
ndmeiri

Reputation: 5029

The issue has to do with your calculation of the index variable in your pageViewController(_:viewControllerBeforeViewController:) and pageViewController(_:viewControllerAfterViewController:) methods. To simplify that logic and the overall logic of your page view controller, you should change your implementation to the following:

class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    private var pages: [UIViewController]!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataSource = self
        self.delegate = self

        self.pages = [
            self.storyboard!.instantiateViewControllerWithIdentifier("FirstNav") as! UINavigationController,
            self.storyboard!.instantiateViewControllerWithIdentifier("SecondNav") as! UINavigationController,
            self.storyboard!.instantiateViewControllerWithIdentifier("ThirdNav") as! UINavigationController,
            self.storyboard!.instantiateViewControllerWithIdentifier("FourthNav") as! UINavigationController
        ]

        let startingViewController = self.pages.first! as UIViewController
        self.setViewControllers([startingViewController], direction: .Forward, animated: false, completion: nil)
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        let index = (self.pages as NSArray).indexOfObject(viewController)

        // if currently displaying last view controller, return nil to indicate that there is no next view controller
        return (index == self.pages.count - 1 ? nil : self.pages[index + 1])
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        let index = (self.pages as NSArray).indexOfObject(viewController)

        // if currently displaying first view controller, return nil to indicate that there is no previous view controller
        return (index == 0 ? nil : self.pages[index - 1])
    }

}

You don't even need to maintain an index instance variable, and this implementation does not do so. But you could, if you wanted. This solution also instantiates a single instance of each UINavigationController instead of instantiating a new one every time the user attempts to scroll to a different page, which conserves memory and preserves the state of the view controllers as the user scrolls between them.

Please excuse the non-descriptive pages variable name. I didn't want to create a conflict with UIPageViewController's viewControllers property.

Upvotes: 2

Related Questions