iOS Swift: UIPageViewController - Turning page programmatically

∥☆過路亽.° 提交于 2019-11-28 20:24:33

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

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 2.2 Example:

extension UIPageViewController {

    func goToNextPage(){

        guard let currentViewController = self.viewControllers?.first else { return }

        guard let nextViewController = dataSource?.pageViewController( self, viewControllerAfterViewController: 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, viewControllerBeforeViewController: 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.

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

}

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

Here is a swift implementation of this:

private func slideToPage(index: Int, completion: (() -> Void)?) {
    let count = //Function to get number of viewControllers
    if index < count {
        if index > currentPageIndex {
            if let vc = viewControllerAtIndex(index) {
                self.pageViewController.setViewControllers([vc], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: { (complete) -> Void in
                    self.currentPageIndex = index
                    completion?()
                })
            }
        } else if index < currentPageIndex {
            if let vc = viewControllerAtIndex(index) {
                self.pageViewController.setViewControllers([vc], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: { (complete) -> Void in
                    self.currentPageIndex = index
                    completion?()
                })
            }
        }
    }
}

viewControllerAtIndex(index: Int) -> UIViewController? is my own function to get the correct view controller to swipe to.

Just leaving this out here incase any one wants to use a defined protocol to navigate around and change view controllers programatically.

@objc protocol AppWalkThroughDelegate {
  @objc optional func goNextPage(forwardTo position: Int)
  @objc optional func goPreviousPage(fowardTo position: Int)
}

Use the above protocol and confirm to root UIPageViewController to manage navigation between view controllers.

Example below:

class AppWalkThroughViewController: UIPageViewController, UIPageViewControllerDataSource, AppWalkThroughDelegate {

    // Add list of view controllers you want to load

    var viewControllerList : [UIViewControllers] = {
       let firstViewController = FirstViewController()
       let secondViewController = SecondViewController() 
       // Assign root view controller as first responder
       secondViewController.delegate = self  

       let thirdViewController = ThirdViewController() 

    }

    override func viewDidLoad() {
       super.viewDidLoad()
       self.dataSource = self
    }

    // Navigate to next page 
    func goNextPage(fowardTo position: Int) {
       let viewController = self.viewControllerList[position]
       setViewControllers([viewController], direction: 
       UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
    }

    }

Once achieved all that, child view controllers that need to make UIPageViewController move to next or previous page can use AppWalkThroughDelegate methods by passing a specific number onto delegate property.

Example below: Delegate method invoked once button pressed

    class SecondViewController: UIViewController {

        var delegate: AppWalkThroughDelegate!

        override func viewDidLoad() {
           super.viewDidLoad()
           self.dataSource = self
        }

         @IBAction func goNextPage(_ sender: Any) {
            // Can be any number but not outside viewControllerList bounds 
            delegate.goNextPage!(fowardTo: 2)
         }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!