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)
}
})
という感じでできました。
以下は原因とかやりたかったことを詳しく書いたので、よかったら読んでみてください。
具体例
UIPageViewControllerにdelegate
とdataSource
をつけて、3ページをスクロールするようにしました。
以下のように実装したとします。
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)
}
}
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ページ目に一気に飛びたい時は以下のようにします。
// 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ページ目)画面しか表示されません。
条件
以下の条件に当てはまると同じ現象が起きるかと思います。
-
UIPageViewControllerの
transitionStyle
に.Scroll
を指定している場合 -
UIPageViewControllerの
setPageViewControllers(_:direction:animated:completion)
でanimated
にtrue
を渡している場合
上記を満たしていると UIPageViewControllerがキャッシュをして他のページを読み込まない事態が発生します。
それを解消するために対応策を講じると、うまくいったという感じです。
最後に
これとっても困りました。
いろいろ調べて解決できて嬉しかったので、書きました。
以上になります。
参考
[Objective-C] UIPageViewControllerのキャッシュが邪魔をする件 | 杏z 学習帳
本当にお世話になりました!