Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Swift: UIPageViewController - Turning page programmatically

I have a UIPageViewController, which works fine when we swipe left or right to turn pages.

class ViewController: UIViewController, UIPageViewControllerDataSource {
...
}

Now I intend to provide Previous/Next button on the page too, so that pages can be turned by clicking on these buttons.

How can I trigger the swipe left/right behaviour OR turn pages programmatically?


Note

This is a question for Swift language, not Objective-C.

like image 630
Jasper Avatar asked May 27 '15 18:05

Jasper


4 Answers

There's generally no need to fiddle around with page indexes and what not. Chances are you are already implementing a dataSource: UIPageViewControllerDataSource, which has all of what you need to get a previous or next ViewController to display.

Swift 5 Example:

extension UIPageViewController {

    func goToNextPage() {
       guard let currentViewController = self.viewControllers?.first else { return }
       guard let nextViewController = dataSource?.pageViewController( self, viewControllerAfter: currentViewController ) else { return }
       setViewControllers([nextViewController], direction: .forward, animated: false, completion: nil)
    }

    func goToPreviousPage() {
       guard let currentViewController = self.viewControllers?.first else { return }
       guard let previousViewController = dataSource?.pageViewController( self, viewControllerBefore: currentViewController ) else { return }
       setViewControllers([previousViewController], direction: .reverse, animated: false, completion: nil)
    }

}

This way you're guaranteed that the pages you're transitioning to are exactly what the PageViewController's built in gestures would trigger.

like image 55
0x6A75616E Avatar answered Oct 17 '22 09:10

0x6A75616E


The Swift 3 version of SuitedSloth's answer (with a small tweak to the animated parameter as I needed it to be animated by default, but still taking a parameter in the function) in case anyone needs it:

extension UIPageViewController {

    func goToNextPage(animated: Bool = true) {
        guard let currentViewController = self.viewControllers?.first else { return }
        guard let nextViewController = dataSource?.pageViewController(self, viewControllerAfter: currentViewController) else { return }
        setViewControllers([nextViewController], direction: .forward, animated: animated, completion: nil)
    }

    func goToPreviousPage(animated: Bool = true) {
        guard let currentViewController = self.viewControllers?.first else { return }
        guard let previousViewController = dataSource?.pageViewController(self, viewControllerBefore: currentViewController) else { return }
        setViewControllers([previousViewController], direction: .reverse, animated: animated, completion: nil)
    }

}
like image 25
Gligor Avatar answered Oct 17 '22 09:10

Gligor


Use this funtion and set the transition style for the animation you want.

enter image description here

enter image description here

like image 9
JordanC Avatar answered Oct 17 '22 08:10

JordanC


Here is the swift 4 implementation with the delegates as well.

Since I also use the UIPageViewControllerDelegate, and the delegate methods weren't called with most of the setViewController solutions.

Thanks to Andrew Duncan's for his comment about the delegate.

// Functions for clicking next and previous in the navbar, Updated for swift 4
@objc func toNextArticle(){
    guard let currentViewController = self.viewControllers?.first else { return }

    guard let nextViewController = dataSource?.pageViewController( self, viewControllerAfter: currentViewController ) else { return }

    // Has to be set like this, since else the delgates for the buttons won't work
    setViewControllers([nextViewController], direction: .forward, animated: true, completion: { completed in self.delegate?.pageViewController?(self, didFinishAnimating: true, previousViewControllers: [], transitionCompleted: completed) })
}

@objc func toPreviousArticle(){
    guard let currentViewController = self.viewControllers?.first else { return }

    guard let previousViewController = dataSource?.pageViewController( self, viewControllerBefore: currentViewController ) else { return }

    // Has to be set like this, since else the delgates for the buttons won't work
    setViewControllers([previousViewController], direction: .reverse, animated: true, completion:{ completed in self.delegate?.pageViewController?(self, didFinishAnimating: true, previousViewControllers: [], transitionCompleted: completed) })
}
like image 9
Vasco Avatar answered Oct 17 '22 08:10

Vasco