LoginSignup
23
27

More than 5 years have passed since last update.

【iOS】UIPageViewControllerでのキャッシュ機能への対応

Last updated at Posted at 2015-04-16

UIPageViewControllerを使って、複数のUIViewControllerをページングするように実装していたところ、ページ切り替えがうまくできなかったのでそれの対処方法の紹介です。

ちなみにですが、この現象 iOS8では発生しませんでした。(iOS シミュレーター調べ)

対応策

先に結論を述べますと、ページ遷移がうまくいきませんでした。

なのでページを自前で切り替える時に以下のようにしたらうまくいきました。

// どっちにアニメーションするか
let direction = .Forward
// 適当なViewController
let viewController = viewControllers[pageIndex]

pageViewController.setViewControllers([viewController], direction: direction, animated: true, completion: { [weak self] finished in
    if !finished {
        return
    }
    dispatch_async(dispatch_get_main_queue()) {
        self?.pageViewControllers([viewController], direction: direction, animated: true, completion:nil)
    }
})

という感じでできました。

以下は原因とかやりたかったことを詳しく書いたので、よかったら読んでみてください。

具体例

UIPageViewControllerdelegatedataSourceをつけて、3ページをスクロールするようにしました。

以下のように実装したとします。

ViewController.swift
class ViewController: UIViewController {

    // 表示するViewController
    lazy var viewControllers: [SubViewController] = {
        var temporaryViewControllers = [SubViewController]
        for var i = 0; i < 3; i++ {
            let viewController = SubViewController.instantiateViewController(text: "Page \(i)")
            temporaryViewControllers.append(viewController)      }
        return temporaryViewControllers
    }()

    lazy var pageViewController: UIPageViewController = {
        let pageViewController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, option: nil)
        pageViewController.delegate = self
        pageViewController.dataSource = self
    }

    private var currentPageIndex: Int = 0

    override func viewDidLoad() {
        setupPageViewController()
    }
}

// MARK: - UIPageViewController dataSource

extension ViewController: UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController: viewController: UIViewController) -> UIViewController? {
        // 今表示しているViewControllerの一つ前にViewControlelrを返す
        // なければnilで返す
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController: viewController: UIViewController) -> UIViewController? {
        // 今表示しているViewControllerの一つ後にViewControlelrを返す
        // なければnilで返す
    }
}

// MARK: - UIPageViewController delegate

extension ViewController: UIPageViewControllerDelegate {

    // ViewControllerのアニメーションなりが終わった段階で呼ばれるメソッド
    func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
        if let viewController = pageViewController.viewControllers.first as? SubViewController {
            // Arrayにfindというメソッドはありません。
            // extensionで自前で拡張しました。
            if let index = viewControllers.find({ $0 == viewController }) {
                currentPageIndex = index
            }
        }
    }

}



// MARK: - setup

extension ViewController {

    private func setupPageViewController() {
        let firstViewController = viewControllers.first!
        pageViewController.setViewControllers([firstViewController], direction: .Forward, animated: false, completion: nil)
        pageViewController.view.frame = view.bounds
        pageViewController.view.autoresizingMask = (.FlexibleHeight | .FlexibleWidth)
        addChildViewController(pageViewController)
        view.addSubview(pageViewController.view)
        pageViewController.didMoveToParentViewController(self)
    }
}
SubViewController.swift
class SubViewController: UIViewController {

    class func instantiateViewController(#text: String) -> SubViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let viewController = storyboard.instantiateViewControllerWithIdentifier("SubViewController") as! SubViewController
        viewController.text = text
        return viewController
    }

    @IBOutlet weak var textLabel: UILabel!

    var text: String!

    override func viewDidLoad() {
        super.viewDidLoad()
        textLabel.text = text
    }

}

というクラスを用意しました。

(細かい処理は省かせていただきました...。)

今回の現象

上記の例をそのまま使います。

現在 1ページ目にいたとしてそこから 3ページ目に一気に飛びたい時は以下のようにします。

ViewController.swift
// MARK : jump page

extension ViewController {

    func jump(#pageIndex: Int) {
        let viewController = viewControllers[pageIndex]
        let direction = (currentPageIndex < pageIndex) ? .Forward : .Reverse
        pageViewController.setViewControllers([viewController], direction: direction, animated: true, completion: nil)
    }
}

こうすれば指定のページへスクロールすることができます。

しかしこの後横スクロールで違う画面へ行くと、 1つ前に表示していた(1ページ目)画面しか表示されません。

条件

以下の条件に当てはまると同じ現象が起きるかと思います。

  • UIPageViewControllertransitionStyle.Scrollを指定している場合
  • UIPageViewControllersetPageViewControllers(_:direction:animated:completion)animatedtrueを渡している場合

上記を満たしていると UIPageViewControllerがキャッシュをして他のページを読み込まない事態が発生します。

それを解消するために対応策を講じると、うまくいったという感じです。

最後に

これとっても困りました。

いろいろ調べて解決できて嬉しかったので、書きました。

以上になります。

参考

[Objective-C] UIPageViewControllerのキャッシュが邪魔をする件 | 杏z 学習帳

本当にお世話になりました!

23
27
0

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
23
27