[Swift] UIPageViewControllerのTipsまとめ (初級編)

# はじめに
久しぶりに、UIPageViewControllerを使う機会があったので、
整理しておきたいと思います。

ちなみに、UIPageViewControllerとは、
ページめくりや紙をめくるようなアニメーションができるViewControllerです。

# Tips
## アニメーションの種類 
UIPageViewControllerTransitionStyle

| 定数 | 説明 |
|---|---|
| .Scroll | ページめくり |
| .PageCurl | 紙めくり |

## めくる方向 
UIPageViewControllerNavigationOrientation

| 定数 | 説明 |
|---|---|
| .Horizontal | 横方向 |
| .Vertical | 縦方向 |

## ページを送る方向

### (1) 順方向
・横方向の場合は、左にスワイプで移動
・縦方向の場合は、上にスワイプで移動

```:サンプルコード
extension SDTPageViewController: UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController,
                            viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        return goFowardPage(viewController)
    }

    func pageViewController(pageViewController: UIPageViewController,
                            viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        return goBackPage(viewController)
    }

    private func goBackPage(viewController: UIViewController) -> UIViewController? {

        var index = (viewController as! SDTPageInnerViewController).pageIndex
        if index == 0 || index == NSNotFound {
            return nil
        }
        index -= 1
        return viewControllerAtIndex(index)
    }

    private func goFowardPage(viewController: UIViewController) -> UIViewController? {

        var index = (viewController as! SDTPageInnerViewController).pageIndex
        
        if index == NSNotFound {
            return nil;
        }
        index += 1
        if index == imageFiles.count {
            return nil;
        }
        return self.viewControllerAtIndex(index)
    }

    private func viewControllerAtIndex(index:Int) -> SDTPageInnerViewController? {

        if imageFiles.count == 0 || index >= imageFiles.count {
            return nil
        }
        if let vc = UIStoryboard.getViewController("Main", identifier: "SDTPageInnerViewController") as? SDTPageInnerViewController {
            vc.pageIndex = index
            vc.filePath = imageFiles[index]
            return vc
        }
        return nil
    }
}

```

### (2) 逆方向
・横方向の場合は、右にスワイプで移動
・縦方向の場合は、下にスワイプで移動

*順方向にめくるパターンとgoFowardPageとgoBackPageを逆にするだけです*

```:サンプル
extension SDTPageViewController: UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController,
                            viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        return goBackPage(viewController)
    }

    func pageViewController(pageViewController: UIPageViewController,
                            viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        return goFowardPage(viewController)
    }
}
```

## 指定ページへ移動する
スライダーと連動させたり、途中から読むなどに利用できます

```:サンプルコード
    func scrollToPage(pageIndex: Int) {
        if let currentViewController = viewControllerAtIndex(pageIndex) {

            if let pageViewController = pageViewController {
                pageViewController.setViewControllers([currentViewController],
                                                       direction: .Forward, animated: false, completion: nil)
            }
        }
    }
```
## ページ番号の取得
独自のページコントローラーやページ番号の表示に利用できます

```:サンプルコード
extension SDTPageViewController: UIPageViewControllerDelegate {

    func pageViewController(pageViewController: UIPageViewController,
                            didFinishAnimating finished: Bool,
                                               previousViewControllers: [UIViewController],
                                               transitionCompleted completed: Bool) {

        if let vc = pageViewController.viewControllers?.first as? SDTPageInnerViewController {

            //ページ番号を表示
            print(vc.pageIndex)
        }
    }
}
```

## オプション
おすすめは、ページ間に余白を指定できるオプションです。
該当ページを表示しているときは、余白が表示されませんが、
ページをめくるとページ間に余白ができます。

| 定数 | 説明 |
|---|---|
| UIPageViewControllerOptionInterPageSpacingKey | 余白を指定する |

<img width="267" alt="スクリーンショット 2016-08-11 0.18.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/71694/40278c70-5252-ab7a-26e9-8ad44cb48264.png">

```:サンプルコード
            pageViewController = UIPageViewController(transitionStyle: .Scroll,
                                                      navigationOrientation: .Horizontal,
                                                      options: [UIPageViewControllerOptionInterPageSpacingKey : 20])

```
## 参考ソースコード
いつも過去に作成したソースコードをコピペしているので、こちらにメモで残しておきます。
横スクロールで、順方向にページがめくれるサンプルです。
（ちなみに、StoryBoardは割愛します。ご了承ください。）

### 土台のViewController

```:SDTPageViewController.swift 
import UIKit

class SDTPageViewController : UIViewController {

    private var pageViewController:UIPageViewController!
    private var imageFiles = ["image01.jpeg","image02.jpeg","image03.jpeg","image04.jpeg","image05.jpeg"]
    private var pageIndex = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

    private func setup() {

        if let startingViewController = viewControllerAtIndex(0) {

            pageViewController = UIPageViewController(transitionStyle: .Scroll,
                                                      navigationOrientation: .Horizontal,
                                                      options: [UIPageViewControllerOptionInterPageSpacingKey : 20])

            if let pageViewController = pageViewController {
                pageViewController.dataSource = self
                pageViewController.delegate = self
                pageViewController.setViewControllers([startingViewController],
                                                      direction: .Forward,
                                                      animated: false,
                                                      completion: {done in})
                pageViewController.view.frame = self.view.frame
                addChildViewController(pageViewController)
                self.view.addSubview(pageViewController.view)
            }
        }
    }

    func scrollToPage(pageIndex: Int) {
        if let currentViewController = viewControllerAtIndex(pageIndex) {

            if let pageViewController = pageViewController {
                pageViewController.setViewControllers([currentViewController],
                                                       direction: .Forward, animated: false, completion: nil)
            }
        }
    }
}

extension SDTPageViewController: UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController,
                            viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        return goFowardPage(viewController)
    }

    func pageViewController(pageViewController: UIPageViewController,
                            viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        return goBackPage(viewController)
    }

    private func goBackPage(viewController: UIViewController) -> UIViewController? {

        var index = (viewController as! SDTPageInnerViewController).pageIndex
        if index == 0 || index == NSNotFound {
            return nil
        }
        index -= 1
        return viewControllerAtIndex(index)
    }

    private func goFowardPage(viewController: UIViewController) -> UIViewController? {

        var index = (viewController as! SDTPageInnerViewController).pageIndex
        
        if index == NSNotFound {
            return nil;
        }
        index += 1
        if index == imageFiles.count {
            return nil;
        }
        return self.viewControllerAtIndex(index)
    }

    private func viewControllerAtIndex(index:Int) -> SDTPageInnerViewController? {

        if imageFiles.count == 0 || index >= imageFiles.count {
            return nil
        }
        if let vc = UIStoryboard.getViewController("Main", identifier: "SDTPageInnerViewController") as? SDTPageInnerViewController {
            vc.pageIndex = index
            vc.filePath = imageFiles[index]
            return vc
        }
        return nil
    }
}

extension SDTPageViewController: UIPageViewControllerDelegate {

    func pageViewController(pageViewController: UIPageViewController,
                            didFinishAnimating finished: Bool,
                                               previousViewControllers: [UIViewController],
                                               transitionCompleted completed: Bool) {

        if let vc = pageViewController.viewControllers?.first as? SDTPageInnerViewController {
            print(vc.pageIndex)
        }
    }
}

extension UIStoryboard {

    static func getViewController<T: UIViewController>(storyboardName: String, identifier: String) -> T? {
        return UIStoryboard(name: storyboardName, bundle: nil).instantiateViewControllerWithIdentifier(identifier) as? T
    }
}
```
### ページ毎のViewController

```:SDTPageInnerViewController.swift
import UIKit
class SDTPageInnerViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    var pageIndex = 0
    var filePath = ""

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

    private func setup() {
        imageView.image = UIImage(named: filePath)
    }
}
```

# まとめ
UIPageViewControllerでよく使うTipsを纏めました。
次回は、ピンチやダブルタップで拡大・縮小する方法を纏めたいと思います。

以上です。