iOSアプリを作ってみる(4) : セグエなのかセグウェイなのか

最初に、今使っている環境についてまったく触れていないことに気がつきました。使っているXCodeのバージョンは4.6.3、ターゲットにしているiOSは6.1です。ARCもオンなので、あまりメモリ管理のことは細かく気にしていません。もちろん、最終的にメモリの使用量などを見ながら必要があれば、不要なモノは消していくなどの対応を取る必要が出てくることは理解しています。

f:id:deutschina:20130906130402p:plain

本日のお題はインターフェイス

普通、iOSのプログラミングの本などをみると画面を作るところ、Interface Builderのところから始まっているものが多いのですが、今回のブログでは全く逆。元となるDBとの接続部分から始めて、最後にようやくユーザインターフェイスの登場です。

おなじみとなった適当に書いた概念図です。

f:id:deutschina:20130906130350j:plain

今回のターゲットは中央部の右端にあるClass UIViewControllerとある部分です。図的にはClassが1つだけに見えますが、実際には使用する画面の数だけClassを用意します。ただ、その前に画面を作ってしまいましょう。

出来るだけ面倒なことはやらないかつ細かいことは後回しにする主義なので、Storyboardを使っています。

f:id:deutschina:20130906130409p:plain

Storyboardの細かい使い方は、他の記事に当たっていただくとして(笑)、いきなり完成図を載せてしまいました。ご覧の通り、画面を5つ用意しました。左上から順に行くと、

  1. スタート画面
  2. 出題画面
  3. 正解画面
  4. 設定画面
  5. 終了画面

いずれの画面もViewの上にラベルやボタン、設定画面の場合はスイッチなどを配置したシンプルな構成になっています。画面上のボタンを押したときに、どのような画面遷移をするかについてはボタンからCtrl+ドラッグでViewまで線をひきました。つまりSegueを使っていますということですね。

ちなみに、これどうやって読むのか悩んだのですが、イタリア語源らしいのでローマ字読みかと思いきや、英語のサイトなどを見るとpronaunce like "seg-way"と書いてあるので、あの乗り物と同じ発音で良いようです。

少しだけ特別なことをやっているとすれば、右上の正解画面から遷移するときにSwipeを使うことにした事ぐらいでしょうか。ボタンだけだと面白くないので、左にSwipeすると次の問題、右にSwipeすると前の問題に戻るようにしました。ただし、出題する問題数が決まっているので、最後の問題からSwipeしたときは終了画面に飛ぶような追加ロジックが必要になります。

ここのSegueの分岐のところで少しハマったので、少し詳しく書いておこうと思います。いろいろ調べても、ネットに転がっている情報が古いのかうまく行きませんでした。そこでもう少しシンプルにやればいんじゃね?と思ったら、その通りうまく行ったという話です。

まずは、ViewControllerのヘッダファイルの冒頭に呪文を1つ書き加えます。

....
#import <UIKit/UIKit.h>

@interface KPTAnswerViewController : UIViewController <UIGestureRecognizerDelegate>
....

これでイベント(Swipe)が発生したときにこのClassに飛んでくる訳ですが、これを忘れていて、ドツボにはまりました。

次のドツボポイントは、Viewに配置したSwipeGestrueReocgnizerから、Ctrl+ドラッグでIBActionを定義したのですが、これも結論から言うと不要でした。単純に、これをViewControllerの中で定義してSegueのアクションを拾ってやれば全く問題ありませんでした。

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
    KPTManageGame *manageGame = [KPTManageGame sharedInstance];
    
    if([segue.identifier isEqualToString:@"AnsToQuizNext"]) {
        if (manageGame.currentNumber == manageGame.numOfWords) {
            NSLog(@"QuitGame");
            [self performSegueWithIdentifier:@"QuitGame" sender:nil];
        } else {
            NSLog(@"AnsToQuizNext");
        }
    } else {
        manageGame.currentNumber -= 1;
        NSLog(@"AnsToQuizPrev");
    }
}

"prepareForSegue"っていうぐらいなので、どのSegueを呼ぶ場合でも必ずここを通るみたいですね。ちなみに、条件によってアクションを変えるのもここでできました。この例の場合は、AnsToQuizNextというSegueが呼び出された時、一定の条件に合致した場合(全問終了した場合)には、別のSegue(QuitGame)を呼ぶという分岐ロジックもここで実装できました。おそらく、分岐後にまたこの部分を通ると思われますが、この場合は条件文がスルーされるので、動作に支障はないはずです。

それでは、Segue自体を呼ばないようにするにはどうするのか?これは、iOS6以降限定のようですがこんなメソッドがあります。

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
    KPTManageGame *manageGame = [KPTManageGame sharedInstance];
    
    if([identifier isEqualToString:@"AnsToQuizPrev"] && manageGame.currentNumber ==0) {
        return NO;
    } else {
        return YES;
    }
}

このメソッドで、NOが返された場合は、Segueの呼び出し自体がキャンセルされます。今回の場合は、実質的に不要だったのですが、予期せぬエラーで落ちることを避ける保険の意味でロジックを追加してみました。

これで大まかなアプリの仕組みは出来ました。実際にテストしても期待通りの動きをしてくれています。次回以降は、もう少し具体的にはまったところについて書いて行こうかと思います。

(つづく)