LoginSignup
12
6

More than 3 years have passed since last update.

UIPageViewControllerでページ遷移時に処理したいことを書くときのTip

Last updated at Posted at 2018-09-29

たまに解説サイトにあるアンチパターン

ページを変わったときにする処理をUIPageViewControllerDataSourcepageViewController関数中に書く解説サイトをいくつか見ます。

extension PageViewController : UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        ... 
        callWhenPageChanged() // ページが変わったら呼びたい処理

        ... /*何かしらのViewControllerをreturnする処理*/
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        ...
        callWhenPageChanged() // ページが変わったら呼びたい処理

        ... /*何かしらのViewControllerをreturnする処理*/
    }
}

callWhenPageChanged()は、例えば、UIPageControl(インジケータ)の現在ページの操作を変更するための処理などが考えられますね。その場合、遷移先になりうるViewControllerの情報が必要になるのも理解できます。

確かにここのpageViewControllerは、左右にフリックしたときなどに、ページデータを提供するために呼ばれるのは確かです。
しかし、あくまでページリソースを扱う前提の関数に、そのような処理を書くのは本質的ではないと私は思います。
というか、本質的でないというか、この実装は間違えています。 この2つの関数は現在のページの前後のページにあたるViewControllerを返す関数です。ページが変わるごとに毎回呼ばれるので、毎ページでcallWhenPageChangedが2回呼ばれますね。

で、何を使えばいいの?

実は、すでにUIPageViewControllerDelegateという素晴らしいDelegateが存在します。
リファレンスにもある通り、UIPageViewControllerDelegateは、以下2つの実装をサポートしています。

// ジェスチャーによる遷移が始まる場合に呼ばれる
// willTransitionTo.firstを見れば、次の遷移先のページのインスタンスを見ることができる
func pageViewController(UIPageViewController, willTransitionTo: [UIViewController])

// ジェスチャーによる遷移が終わった場合に呼ばれる
func pageViewController(UIPageViewController, didFinishAnimating: Bool, 
                        previousViewControllers: [UIViewController], 
                        transitionCompleted: Bool)

これらは、ジェスチャーによる画面遷移イベントが発生した呼ばれる関数です。こちらのどちらかに書くほうが適してますね。

現在のページ(ViewController)を取得したい

具体的に、callWhenPageChangedを使いたいときの例を書いてみます。

extension PageViewController: UIPageViewControllerDelegate { 
        // ジェスチャーによる遷移が終わった場合に呼ばれる
        func pageViewController(_ viewController: UIPageViewController, didFinishAnimating: Bool, 
                                previousViewControllers: [UIViewController], 
                                transitionCompleted: Bool) {
                ...
                callWhenPageChanged() // ページが変わったら呼びたい処理

                ...
        }
}

では、現在表示されているページ(ViewController)を取得を取得したい場合はどうすればいよいでしょうか?
上記の関数(didFinishAnimatingがある方)では、第1引数のviewController.viewControllers.firstが現在表示されているページに当たりますので、それを見てもらえばよいです。

追記

以下 @azuma317 さんからコメントを頂いたのでご紹介します。

DataSourceなので、ページ構成時に呼ばれそうだけど、意外と呼ばれなかったりしますね...
遷移後の処理はDelegateに書くべきだけど、transitionCompletedがtrueのときじゃないと、画面遷移を失敗したときにも呼ばれるっぽいです。

PageViewController.swift
// Delegate内
if completed {
    callWhenPageChanged()
}

もし画面遷移が行われないときにcallWhenPageChangedが呼ばれるとまずいケースもあると思うので、completedで分岐させるのは必要ですね。

最後に

UIPageViewControllerDelegateの使い方をちょっとだけ解説しました。間違っていることがあればご報告お願いします。

12
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6