UIPageViewControllerの使い方 -基礎-
iOSで電子書籍のようなUIを作りたい場合、UIPageViewControllerという素晴らしいUIが存在します。これを使えば、ページ移動時に「ページをペラッとめくってる」や「横へスクロール」するようなアニメーションが簡単に実装できちゃいます。とは言うものの、UIPageViewControllerは構造が少し複雑で、それを把握せずに実装しちゃうと意味不明な詰まり方をします。
そこで基礎編/Tips編と分けて自分なりに整理して書き残します。今回は基礎編なので、ざっくりとした考え方と使い方の説明となります。
UIPageViewControllerで何が出来るか
何が出来るのかについて列挙します。
テンプレートを眺めてみる
UIPageViewControllerを知るにはAppleが用意しているテンプレートのコードを追う方法が一番手っ取り早いです。
Xcodeの新規プロジェクト作成から「Page-Baseed Application」を選択して、まずはテンプレートを元にしたプロジェクトを作成してください。
テンプレートのソースファイル構成
作成されたソースファイルを確認してみてください。Xcode ver 4.6.1では以下のように作成されます。
UIPageViewControllerのインスタンス生成と初期化
MainStoryboard_iPhone.storyboard と RootViewController.m を参照ください。storyboard上に設置されたUIPageViewControllerをRootViewController.mのviewDidLoadメソッド内で以下のような初期設定が行われています。インラインで説明すると書きにくかったのでコード上にコメントを書いてます。
// インスタンス生成 self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil]; // 指定内容に関しては次回説明します
// UIPageViewControllerDelegateプロトコル self.pageViewController.delegate = self; // ページめくり開始/終了などのdelegate methodがあるのでコントロールしたい場合は設定しとく
// 表示するページを設定 DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard]; // storyboard上で定義しているページ画面 NSArray *viewControllers = @[startingViewController]; [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
// 管理クラスを設定 self.pageViewController.dataSource = self.modelController;
// addChildViewControllerした後にする必要があるオマジナイ的なヤツ [self.pageViewController didMoveToParentViewController:self];
// ジェスチャー設定 self.view.gestureRecognizers = self.pageViewController.gestureRecognizers; // UIPageViewControllerが行うジェスチャーをInterceptする事もできる
ページをめくり
ModelController.m を参照ください。先ほどと同じようにコードにコメント書いて説明します。
- (id)init { self = [super init]; if (self) { // ページに表示するデータの初期化。サンプルアプリが月名をページに表示してるので、ここは月データを扱ってるというだけ // RootViewController.mでModelControllerのインスタンス生成時にページデータを渡し、ここでページデータを保持する方法を取る NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; _pageData = [[dateFormatter monthSymbols] copy]; } return self; }
// ページのインスタンスを生成するメソッド。ページ移動した時などに使われる - (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard { // indexの値がページ情報の件数と整合性が取れてるか確認してるだけ if (([self.pageData count] == 0) || (index >= [self.pageData count])) { return nil; } // ページのインスタンスを生成。ここでinitで保持しておいたページ情報を受け渡す // これでDataViewControllerの内容がUIPageViewController上に表示される DataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:@"DataViewController"]; dataViewController.dataObject = self.pageData[index]; return dataViewController; }
// privateなメソッドで、引数のvewControllerがどのページかを返してるだけ - (NSUInteger)indexOfViewController:(DataViewController *)viewController { return [self.pageData indexOfObject:viewController.dataObject]; }
// UIPageViewController上で右スワイプした時に呼ばれるメソッド // 勘違いしやすいのは名前にbeforeと付いているからといって必ずしも前ページへの移動の時だけに呼ばれるという訳ではないです // 左開きの時は戻る場合、右開きの時は進む場合に呼ばれます - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { // ページの整合性をチェック // 左開きか右開きかでここの処理は変わり、以下のコードは左開きの場合の処理です NSInteger page = [self indexOfViewController:(MangaDataViewController *)viewController]; if (page == NSNotFound) { return nil; } if (page == 0) { return nil; } page--; // 前述したようにページのインスタンス生成を行う return [self viewControllerAtIndex:page storyboard:viewController.storyboard]; }
// UIPageViewController上で左スワイプした時に呼ばれるメソッド // 全てにおいて先ほどのメソッドと逆になります // 左開きの時は進む場合、右開きの時は戻る場合に呼ばれます。 - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { NSInteger page = [self indexOfViewController:(MangaDataViewController *)viewController]; if (page == NSNotFound) { return nil; } if (page == [self.pageData count] + 1) { return nil; } page++; return [self viewControllerAtIndex:page storyboard:viewController.storyboard]; }
まとめ
サンプルを眺めたお陰でUIPageViewControllerの構成が理解出来た気になれたかと思います。
次のTips編ではサンプルプロジェクトをベースに電子書籍アプリでやりたいであろう機能をTips形式で列挙していく予定です。
追記(2013/04/22)
Tips編を書きました。