OpenGLES のアプリを作成していても、設定画面はアップルが提供している UI を使うのが簡単だし、ユーザーとしてもわかりやすいからいいよね、というのは当然考えるところ。しかしながら、OpenGL 関連一辺倒でやってきたので UIなんたらController とか UINavigationController とか実はちゃんとはよくわかっていません。
もう1つ。UINavigationController や UITableViewController などに関する情報は見つけられるのですが、大元の UIViewController を扱った情報がなかなか出てきません。じゃあやってみましょうか、というのがこの記事を書く理由ですね。
本記事内容についての正確性については保証しかねますので実際のところは各自リファレンスなどでご確認ください。
<前提知識>
まず、Ray Wenderlich のチュートリアルのうち、Beginning Storyboards in iOS 5 の Part 1 と Part2 を読みました。コードをよく見ていくと、delegate をどうやって使っているのかがよくわかりますね。個人的には View (+ ViewController) の扱いを Flash のディスプレイ表示リストになぞらえて捉えると理解がよく通りました。
しかし、画面遷移に segue というモノを使っており、StoryBoards ありきの話になってしまっています。もちろんそこを目的としているチュートリアルなので問題ないのですが、個人的には
- メインの GLKView (=OpenGL) 上のタップを検出して設定画面に遷移させたい
- GLKView の表側には CocoaTouch 系のボタンなどは置きたくない
(パフォーマンス+画面を広く使いたいから) - よってGlkView 上のタップ位置を検出して遷移させたい
- メインの View の最初の表示は StoryBoard でつくったものを使う
という方向性がありました。よって segue 君は使えません。
web 上をいろいろと探したのですが、良いチュートリアルが見つかりません。Apple 提供のドキュメント類、とくに「iOS View Controller プログラミングガイド」がよさそうったのですが、キーワードで中身を検索したら思っているような事柄が少ない感じ。だったら自分でコードを書いて確かめたほうが方が速いかな、ということで。
開発環境
・Xcode 4.3.1
・ARC ありき
・iPhone 5.1 simulator
参考サイト:http://d.hatena.ne.jp/glass-_-onion/20120601/1338477967
<プロジェクト作成>
File –> New –> Project –> Single View Application
と、設定して Create。
MainStoryBoard.storyboard を表示し、View の部分をクリックすると(少し灰色のアウトランが付く)、View の情報を確認できます。クラスはデフォルトの UIView になっています。
次に、View の下部にある黒いバーを選択すると(アウトラインが青になる)、ViewController の情報が表示され、Project 生成時に自動的に作られたクラスが当てはめられいてます。
この状態では RACViewController.h と RACViewController.m に書いた情報がこの View が持つ機能となります。逆に言うと、StoryBoard 上のビューに何らかの機能を持たせたければ、その機能を持ったクラスの名前をここに書けば良いことになりますね。
<ウインドウを追加する>
さて、ではこの View にボタン (Round Rec Button) を追加しましょう。名前は change。画面を遷移させる=切り替えるボタンです。次に、このボタンが押された時の処理を RACViewController.h に宣言しましょう。(メソッドのネーミングが Apple 的でないかもしれませんね。。。)
// add to RACViewController.h - (IBAction)changeToSecondView:(id)sender;
StoryBoard にもどり、ボタンを Ctrl キーを押しながらドラッグしていって、下のバーの中の ViewController のアイコン(黄色の方)にドロップします。
続いて黒いポップアップが出てくるので先ほど追加したメソッド changeToSccondView: を選択。
これでボタンが押されると changeToSecondView: が呼ばれる、という関連付けがなされました。
ではこのメソッドの内容を書いていきましょう。
参考にした glass-_-onion さんのサイトを参考にして書くと以下のようになりました。
// add to RACViewController.m - (IBAction)changeToSecondView:(id)sender { NSLog(@"change"); UIViewController *controller = [[UIViewController alloc] init]; controller.view.backgroundColor = [UIColor blueColor]; [self presentViewController:controller animated:YES completion:nil]; }
新しいコントローラーを作成し、背景色を青に変更。そのコントローラーを present、というのが流れ。View の設定は必要ないみたいです。それからメソッド名にある “present” は「現在の」という意味かとおもってましたが、「出演させる」という意味もあるようで、そちらの方みたいですね。
とにかく、ボタンを押すと新しい青い画面がアニメーション付きでせり上がってきます。
<ウインドウの除去と情報の取得>
さて、この辺で UIViewController のリファレンスを確認してみましょう。関連するメソッド群としては
- Presenting Another View Controller’s Content
- Getting Other Related View Controllers
あたり。この中で present した View を除去するメソッド
– dismissViewControllerAnimated:completion:
について見ると、
The presenting view controller is responsible for dismissing the view controller it presented.
(present した view controller は、自身が present した view controller を始末する責任を負う。)
という記述が見られます。まどろっこしい和訳ですが、つまりAというViewController が presentViewController: メソッドで ViewController を追加したら、その追加したものについてはAがちゃんと始末しろよ、ということのようです。余談。
実験実験。本当は追加する ViewController をサブクラス化するほうがよいのですが、面倒なのでタイマーでやってみます。まずは先程のメソッドを書き換え。といか最後の1行を追加。
// revise at RACViewController.m - (IBAction)changeToSecondView:(id)sender { NSLog(@"change"); UIViewController *controller = [[UIViewController alloc] init]; controller.view.backgroundColor = [UIColor blueColor]; [self presentViewController:controller animated:YES completion:nil]; [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:@selector(onTime) userInfo:nil repeats:YES]; }
それからタイマーで実行されるメソッドの追加。
- (void)onTime { NSString *className = NSStringFromClass([[self presentedViewController] class]); NSLog(@"my presented : %@", className); static BOOL flag = YES; static int count = 0; if (flag) { count++; if (count >= 10) { [self dismissViewControllerAnimated:YES completion:nil]; flag = NO; } } }
5秒経過したら self が present した ViewController を破棄しています。ついでに0.5秒ごとに presentedViewController を表示してみています。青いウインドウが消えるまでは UIViewContollerと表示され、そのあとは nil と表示されます。
ちなみにリファレンスによると present は複数回可能であり、dissmissViewController のメソッドでは preset されたものを全部破棄するようです。もし1つ1つ管理したいのであれば、リファレンスの “Managing Child View Controllers in a Custom Container” のメソッド群を利用するとよさそうです。ただし Available in iOS 5.0 and later だそうで。
< StoryBoard で作った ViewController を利用する>
ここまではメッソドの中で自前で alloc + init でもって新しい ViewController を作っていましたが、画面は StoryBoard で作ってしまいたいですよね。さて、適当で良いのでもう1つ ViewController を StoryBoard 上に作成しましょう。
新しいビューの下部にある黒いバーをクリック(青アウトラインが出る)。右上のウインドウで Identifier を入力しましょう。ここでは subView としました。
ここからの手順は以下のサイトを参考にしました。意外と簡単ですね。
http://tech.caph.jp/2012/02/02/storyboard-nav-customize/
再度、RACViewController.m を書き換えます。
- (IBAction)changeToSecondView:(id)sender { NSLog(@"change"); UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"subView"]; [self presentViewController:controller animated:YES completion:nil]; [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:@selector(onTime) userInfo:nil repeats:YES]; }
self が参照している StoryBoard から Identifier が “subView” というものを取ってきて、それを表示するという流れです。あとは一緒で5秒後に追加した View が消えます。
=== 以上 ===
文章としては長くなってしまいましたが、ViewController の追加と除去についての整理がつきました。間違いなどありましたらご指摘ください。