iOS Swift: UIPageViewController - 以编程方式翻页

29

我有一个UIPageViewController,在我们向左或向右滑动翻页时工作正常。

class ViewController: UIViewController, UIPageViewControllerDataSource {
...
}

现在我打算在页面上提供“上一页/下一页”按钮,以便通过点击这些按钮来翻页。

我应该如何在程序中触发左滑/右滑的行为或者实现页面翻转呢?


注意

这是一个有关Swift语言的问题,不涉及Objective-C。


6个回答

72

通常情况下不需要调整页面索引等内容。很可能您已经实现了 dataSource: UIPageViewControllerDataSource,它已经包含了您需要显示上一个或下一个视图控制器的所有内容。

Swift 5 示例:

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

}

这样你就能确保你要过渡到的页面正好是PageViewController内置手势会触发的内容。


3
我很难弄清楚如何使用你的方法更新presentationIndexForPageViewController。在我的pageItemViewController中,我声明了一个叫做itemIndex的变量。我该如何在这个扩展中设置它? - Adrian
我使用了带定时器的 func goToNextPage() 方法 - 那么我该如何使用这个方法来带有定时器地滚动到下一页呢?这是我的问题链接 https://dev59.com/D6Pia4cB1Zd3GeqPwlAZ?noredirect=1#comment77027842_45025088 - Saeed Rahmatolahi
救命稻草!!!非常感谢。我将发布Swift 3版本,以供将来参考。 - Gligor
1
干得好,以我个人的看法,这展示了UPPageViewController中的一个缺陷。 :) - William T. Mallard
2
快速提示:这种方法似乎不会触发UIPageViewControllerDelegate(:didFinishAnimating),而当使用手势进行分页时,它确实被调用,我认为这更像是一个错误而不是一个特性。 - William T. Mallard

25

以下是 SuitedSloth 的回答的 Swift 3 版本(对 animated 参数进行了微小调整,以使其默认情况下具有动画效果,但仍可在函数中接受参数),如果有人需要:

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

}

1
非常感谢! - William T. Mallard
很开心能帮到你 @WilliamT.Mallard - Gligor
6
如果你正在更新委托中的点,你可能希望使用以下代码来完成(而不是使用nil):{ completed in self.delegate?.pageViewController?(self, didFinishAnimating: true, previousViewControllers: [], transitionCompleted: completed) } - Andrew Duncan
@Recusiwe,请检查你上面的评论。 - Gligor
1
@CristianMoisei 我也遇到了同样的问题,但我做的是正确格式化闭包。如果它在单行上,则无法工作,必须正确格式化:completed in,然后在新行上是 self.delegate?.pageViewController... - josher932
显示剩余2条评论

10
使用此函数并设置所需动画的过渡样式。 enter image description here enter image description here

1
JordanC> 我需要在按钮点击时翻页 - 那么我该如何使用上述函数? - Jasper
2
@Jasper 创建一个UIButton,当它被点击时,你需要确定在UIPageViewController中的下一页是哪一页,并将该页面传递给setViewControllers方法。 - JordanC
我不知道为什么,但我得到了正确的页面,但如果我调用这个方法,什么也不会改变。 - Lennart P.

9

这里是使用委托的Swift 4实现。

由于我还使用了UIPageViewControllerDelegate,并且大多数setViewController解决方案都没有调用委托方法。

感谢Andrew Duncan的评论,关于委托的问题。

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

6
这是一个swift实现的例子:
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? 是我自己编写的函数,用于获取正确的视图控制器以进行滑动。


currentPageIndex 是从哪里来的? - ataravati
1
currentPageIndex 是一个本地变量,您可以定义它以便跟踪当前页面。 - Swinny89

2

在这里留下一些内容,以防任何人想要使用定义的协议来编程地导航和更改视图控制器。

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

使用上述协议并确认根UIPageViewController来管理视图控制器之间的导航。

下面是一个示例:

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

    }

完成上述步骤后,需要使UIPageViewController向前或向后移动的子视图控制器可以通过将特定数字传递给委托属性来使用AppWalkThroughDelegate方法。

以下是示例:按钮按下后调用的委托方法

    class SecondViewController: UIViewController {
       
        var delegate: AppWalkThroughDelegate!
        
        override func viewDidLoad() {
           super.viewDidLoad()
        }

         @IBAction func goNextPage(_ sender: Any) {
            // Can be any number but not outside viewControllerList bounds 
            delegate.goNextPage!(fowardTo: 2)
         }
    }

在第二个例子中,为什么要设置self.dataSource = self? - J Derbs
可能是个错误,但不应该在那里,感谢指出。@JDerbs - Sharkes Monken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接