Swift UIPageViewControllerのジェスチャ完了のイベントを取得する


概要

UIPageViewControllerDataSourceのviewControllerBeforeviewControllerAfterメソッドで前後のViewControllerを切り替える場合、両メソッドはジェスチャ途中で発生する為、予期せぬプロパティの保持結果となってしまうケースがある。


ダメなケース

下の例ではスクロールジェスチャ途中でキャンセルした場合でもイベントが着火している場合があり、メンバのindexが操作されてしまい、次回完了時にindexの値が飛んでいるケースが発生する。

extension ViewController: UIPageViewControllerDelegate {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        self.index -= 1
        let vc = MyViewController()
        vc.index = self.index
        return vc
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        self.index += 1
        let vc = MyViewController()
        vc.index = self.index
        return vc
    }
}


スクロール完了ジェスチャを捕捉する

UIPageViewControllerDelegateのdidFinishAnimatingメソッドを使用する

extension ViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    //ジェスチャ完了した時のみ保持するパラメータを確定させる
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        if completed {
            self.index = self. pendingIndex
        }
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        self. pendingIndex  = self.index - 1
        let vc = MyViewController()
        vc.index = self. pendingIndex
        return vc
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        self. pendingIndex = self.index + 1
        let vc = MyViewController()
        vc.index = self. pendingIndex
        return vc
    }
}
参考

scior.hatenablog.com