はじめに
チュートリアルでよくあるスワイプでページ切り替えについてです。
もともと、ページングのためにpageViewControllerが用意されていますが、使おうとすると、非常に煩雑なコードを多く書かなければなりません。(メインのコードが埋もれる)
この記事は、メインのコードを、簡潔化させるのに役立ちます。
実装的に魔法のような部分が多いですが(すみません)、メインのコードをかなり簡潔化できます!
環境
iOS
Swift 2.0 (Swift1.2のコードを、Xcode7.0で動作可能なように変更しただけです)
Xcode 7.0
サンプル
サンプル(ライブラリ?含む)はGitubで提供しています
ha1fha1f/PagingViewController
gitに慣れている方はお好きなところにclone、
慣れてない方はターミナルを起動して以下のコマンドを実行してください。DesktopにPagingViewControllerフォルダができるかと思います。
cd ~/Desktop
git clone https://github.com/ha1fha1f/PagingViewController.git
そのままサンプルのプロジェクトになっているので、実行可能です。
PagingSample内がサンプル、PagingLibないがライブラリです。
このサンプルでは、チュートリアルのように5枚の写真が出てきて、最後のページだけStartボタンがある。そのStartを押すとViewControllerへと画面遷移する、という流れになっています。
最小限の利用方法
サンプルはほぼ最小構成になっています。
-
PagingViewController/PagingViewControllerLibにある3つの.swiftファイルをプロジェクトに追加
- PagingViewController.swift
- PagingDataController.swift
- PagingCellViewController.swift
-
PagingCellViewControllerを継承したクラスをつくる
これが各ページになります。
最低限、override func setDataObject(dataObject: AnyObject?) {}
をoverrideして、受け取ったdataObjectを適切にキャストしてフィールドとして保存してください
その他は通常通りで、特殊な処理を書く必要はありません。サンプルを以下に示します。
class ImagePageCellViewController: PagingCellViewController{ var imagePath: String! override func setDataObject(dataObject: AnyObject?){ if let tmpDataObject = dataObject { self.imagePath = tmpDataObject as! String }else{ self.imagePath = "default.jpg" } } }
-
PagingDataControllerを継承したクラスをつくる
これが全てのページを管理するクラスのサポートをします。
最低限、override func instanciateViewControllerAtIndex(index: Int) -> PagingCellViewController? {}
をoverrideして、indexに対応した、PagingCellViewControllerを継承したクラスのインスタンスを生成してreturnしてください。
indexは何ページ目かを示します(0〜)サンプルを以下に示します
class TutorialPageDataController: PagingDataController { override func instanciateViewControllerAtIndex(index: Int) -> PagingCellViewController? { let dataViewController: PagingCellViewController // 最後のページだけ変えるサンプル if index == (self.pageData.count-1) { dataViewController = ImagePageCellLastViewController() }else{ dataViewController = ImagePageCellViewController() } return dataViewController } }
-
PagingViewControllerを継承したクラスをつくる
これが全てのページをsubViewとしてもつViewControllerクラスになります。以下、親Controllerと呼びます。これもUIViewControllerを継承しています。
最低限、PagingDataControllerを継承したクラスのインスタンスを作成して、setupWithDataControllerを実行する必要があります。サンプルを以下に示します
class TutorialViewController: PagingViewController { override func viewDidLoad() { super.viewDidLoad() let data = ["pic1.jpg", "pic2.jpg", "pic3.jpg", "pic4.jpg", "pic5.jpg"] self.setupWithDataController(TutorialPageDataController(data: data) } }
-
これで最低限のページング処理を実現できます!
詳細
PagingCellViewController(を継承したクラス)
self.sendMessageToRoot("test")
管理しているViewControllerに対してメッセージ(String)を送れます(正確には、親ControllerクラスのreceiveMessage()メソッドを呼び出しています)
self.delegateに親Controllerを入れてあるので、これを使うこともできます
self.indexを見ると自分が何ページ目なのか取得できます
PagingDataControllerでindexに応じて異なるクラスを作成することができるので、継承したクラスはいくつあっても問題ありません。
PagingViewController(を継承したクラス)
これが親のcontrollerなので、この背景が、ページをスワイプしきったときの背景になります。逆に、どのページが表示されている時にも不動のもの(pageControlなど)を表示させることもできます。
ページングに関する設定項目は、
// ページの切り替わりのアニメーション
self.trasitionStyle = UIPageViewControllerTransitionStyle.Scroll
// ページ切り替わりの方向(縦横)
self.navigationOrientation = UIPageViewControllerNavigationOrientation.Horizontal
// PageControl(点が動く奴)が不要なときは
self.pageControl.removeFromSuperview()
self.pageControlの位置やサイズは、普通にいじれます
利用できるメソッドとしては、
// 特定のページヘジャンプする
moveTargetPage(index: Int)
// 今何ページ目を見ているか
getCurrentPageIndex() -> Int!
// 今見ているViewControllerのインスタンスを取得
getCurrentViewController() -> PagingCellViewController
オーバーライドできるメソッドとしては、
// 管理下のpageCellからのメッセージを受け取る
override func receiveMessage(message: String) {}
// ページ遷移後に呼ばれる。fromとtoはindexが渡される
override func afterPaging(from: Int, to: Int) {}
// ページ遷移前に呼ばれる。fromとtoはindexが渡される
override func beforePaging(from: Int, to: Int) {}
があります。
※いまPageControl用にafterPagingを用いているので、afterPagingのみsuper.afterPaging(from, to: to)を呼び出すようにお願いします
おわりに
PageControlをライブラリに組み込む必要はなかったかもしれません、笑
Cocoapodsでインストールできるようにする、みたいなことはできるんでしょうか