UIPageViewControllerを使ってみた
目的
既存のアプリの画像プレビュー画面をつくるため
状況として
- NavigationControllerとTabControllerのついたViewからpushされる
- AutoLayoutを使用している
この2つの時。ちょっとつまづくところや自分で工夫してみたところがあるので
導入方法
- StoryboardにPageViewControllerを設置してsegueを貼る
- Storyboardに表示したいページになるViewControllerを設置する
- 今回はAutoLayoutがナビゲーションバーの高さとタブバーの高さに依存してほしいから子となるViewControllerにPageViewControllerから適当に使わないsegueを貼った(もっといい方法あれば教えて下さい。。。)
- 子のViewControllerは画像を表示するだけなのでOutletだけ接続しWillAppearの段階で画像を反映させるようにするだけでオーケー
- PageViewControllerにはDataSourceとDelegateを継承して設定(デフォルトのサンプルだとDataSourceとDelegateの継承クラスは2つにわけてるけど今回はいっぺんに)
コードはこんな感じです。Utility以下は必ず作らないといけないわけではなくて何らかの形でその処理の内容が実現できてればいいです。
import UIKit
class ImagePreViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageData: NSMutableArray!
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
let startViewController: PreViewController = self.viewControllerAtIndex(0, storyboard: self.storyboard!)!
let viewControllers = [startViewController]
self.setViewControllers(viewControllers, direction: .Forward, animated: false, completion: {done in })
self.dataSource = self
self.view.gestureRecognizers = self.gestureRecognizers
}
// pageViewController関連----------------------------------------------------
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index = self.indexOfViewController(viewController as! PreViewController)
if index == 0 || index == NSNotFound {
return nil
}
index--
return self.viewControllerAtIndex(index, storyboard: viewController.storyboard!)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index = self.indexOfViewController(viewController as! PreViewController)
if index == self.pageData.count - 1 || index == NSNotFound {
return nil
}
index++
return self.viewControllerAtIndex(index, storyboard: viewController.storyboard!)
}
// 向きはPortrait限定なので常に表示されるページは一個
func pageViewController(pageViewController: UIPageViewController, spineLocationForInterfaceOrientation orientation: UIInterfaceOrientation) -> UIPageViewControllerSpineLocation {
let currentViewController = self.viewControllers![0]
let viewControllers = [currentViewController]
self.setViewControllers(viewControllers, direction: .Forward, animated: true, completion: {done in })
self.doubleSided = false
return .Min
}
// Utility------------------------------------------------------------------
func indexOfViewController(viewController: PreViewController) -> Int {
if let dataObject: AnyObject = viewController.imageData {
return self.pageData.indexOfObject(dataObject)
} else {
return NSNotFound
}
}
func viewControllerAtIndex(index: Int, storyboard: UIStoryboard) -> PreViewController? {
if self.pageData.count == 0 || index >= self.pageData.count {
return nil
}
let preViewController = storyboard.instantiateViewControllerWithIdentifier("PreViewController") as! PreViewController
preViewController.imageData = self.pageData[index] as! NSData
return preViewController
}
}
pageViewControllerのviewControllerBeforeうんにゃらとviewControllerAfterうんにゃらが前後のViewを決めるdelegateメソッドになっています。内容はわりとここを参考にしましたがようは今表示されてるViewが用意されてるViewの何枚目かを判断して決めているだけです。実際にはpageDataに画像のNSDataをぶっこむ処理をsegueのところでやっています。
NavigationController+TabBarController+PageViewController
AutoLayoutのための工夫は先程言いました。しかしなにもしないままだと実はタップするまでNavigationBarの下にViewが表示されてしまいその後潜るという挙動が発生します。
このことに関してはここの追記を参考にAdjust Scroll View Insetsのチェックを外すことで直せました。