问题
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.
回答1:
Use this funtion and set the transition style for the animation you want.
回答2:
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.
回答3:
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)
}
}
回答4:
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) })
}
回答5:
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.
回答6:
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)
}
}
来源:https://stackoverflow.com/questions/30489920/ios-swift-uipageviewcontroller-turning-page-programmatically