[Swift] UIPageViewControllerのself.setViewControllersをしても反映されない時に試すこと


W杯日本対セネガル見ながら書いてます

TLTR

  1. dataSourceを入れ直す
  2. setViewControllersのanimatedをfalseにする

PageViewControllerのsetViewControllersが反映されないのは、キャッシュが残ったままなのが主な原因かなと思います
なので...

キャッシュをクリアする

dataSourceを入れ直す

PageViewControllerはページ間の移動をスムーズにするために、前後のViewControllerがキャッシュされています

ここで例えば、TabBarControllerで2つタブがあるアプリがあって、
タブ1にPageViewController、タブ2に別のViewControllerだとします

タブ1のPageViewControllerは下の感じ

example.swift
import UIKit

class PageViewController: UIPageViewController {
    override func viewDidLoad() {
        // dataSourceの実装をこのクラスに任せる
        self.dataSource = self

        // storybordのStorybord IDからインスタンス化
        let timelineViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        // そのViewControllerをPageViewControllerに表示する
        self.setViewControllers([timelineViewController], direction: .forward, animated: true, completion: nil)

    }

    override func viewWillAppear() {
        // 表示されるViewControllerをリセットする
        let timelineViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        self.setViewControllers([timelineViewController], direction: .forward, animated: true, completion: nil)
    }
}

extension PageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        let timelineViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        return timelineViewController

    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        let timelineViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        return timelineViewController

    }
}


このようなアプリでタブ1->タブ2->タブ1という感じでViewControllerを切り替えると、
まずタブ1のPageViewControllerで表示中のViewControllerの前後のViewControllerがキャッシュさるので、タブ2からタブ1に戻ってきたときにviewWillAppearで表示するViewControllerをリセットしようとしても、キャッシュされている前後のViewControllerが左右にフリックすると表示されて、うまくリセットできないことがあります

なので、PageViewControllerないのViewControllerをリセットしたいときはキャッシュもクリアにする必要があります

そのときに使えるのが一度self.dataSourceを入れ直すという技です

example.swift
import UIKit

class PageViewController: UIPageViewController {
    override func viewDidLoad() {
        // 同じ
    }

    override func viewWillAppear() {
        // 表示されるViewControllerをリセットする
        let timelineViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        self.setViewControllers([timelineViewController], direction: .forward, animated: true, completion: nil)

        // dataSourceを入れ直す
        self.dataSource = nil
        self.dataSource = self
    }

}

これでキャッシュもクリアされて、意図したようにしっかりViewControllerたちがリセットされます

他の方法

setViewControllersのanimatedをfalseにすると、dataSourceを入れ直さなくてもしっかりリセットされます

example.swift
import UIKit

class PageViewController: UIPageViewController {
    override func viewDidLoad() {
        // dataSourceの実装をこのクラスに任せる
        self.dataSource = self

        // 関数にまとめた
        self.setPage()
    }

    override func viewWillAppear() {
        self.setPage()
    }

    func setPage() {
        let timelineViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        // animatedをfalseにする
        self.setViewControllers([timelineViewController], direction: .forward, animated: false, completion: nil)

    }

}

これはまだ推測なのですが、trueではキャッシュをクリアしないといけなくて、falseはそうではないことから、
animatedをfalseにすることで、ページ移動のアニメーションがなくなり、
ページ間のフリックが軽くなって、キャッシュする必要がなくなるので、同時にキャッシュをクリアする必要もなくなるのかな~
なんて思ってます

以上
日本がんば

Twitter: https://twitter.com/_tomocy