ToDoリストiPhoneアプリを作成してみよう(Part3)

みなさんこんにちは。Yuta Fujii(@HofuCamera)です。

今回で「ToDoリストiPhoneアプリを開発しよう」のPart3となります。

ToDoリストiPhoneアプリを作成してみよう(Part1)
ToDoリストiPhoneアプリを作成してみよう(Part2)

Part3では、前回アプリ内にインプットしたデータを「取り出して」、「表示する」ということを行っていきたいと思います。これが今回のゴールです。

《前回までの講座》


iPhoneアプリ開発の環境を整えよう
iPhoneアプリ開発に必要なこと
プログラムなしで、 レッドカードiPhoneアプリを開発してみよう
プログラムなしで、カラフルカードアプリを開発してみよう
お気に入りのサイト集アプリを開発してみよう
ノートメールアプリを開発してみよう(Part1)
ノートメールアプリを開発してみよう(Part2)
10秒で止める「ジャストタイムiPhoneアプリ」を開発してみよう(Part1)
10秒ジャストで止めるゲーム、ジャストタイムアプリを開発してみよう(Part2)
嘘電話アプリを開発してみよう
パスワードiPhoneアプリ作成してみよう(Part1)
パスワードiPhoneアプリ作成してみよう(Part2)


ライフサイクルについて

-(void)viewWillAppear:(BOOL)animated

まず、今まで行ってきた記事の中で必ずプロジェクトのはじめに記載されているのが、「viewDidLoad」だったと思います。これは、画面が表示されたときにはじめに呼ばれるが1回しか呼ばれないというルールがあったと思います。

今回扱う「viewWillAppear」というメソッドは、画面が表示される度に呼ばれます。

これ以外にもいろいろとあるのですが、呼ばれる順番はviewDidLoad→viewWillAppearとなります。
このように決まって呼ばれる順番のことを「ライフサイクル」と呼びます。

さまざまあるので、興味のある方は「iOS,ライフサイクル」と検索してみてください。

アプリ内から配列を取り出す

NSMutableArrayの初期化

それでは、続きを行っていきましょう。

まずはじめに、アプリ内に保存されたデータを出す準備をします。
NSUserDefaultsを宣言したあと、viewDidLoad内に初期化します。
xcode_todo1


ud = [NSUserDefaults standardUserDefaults];

次に、下記のようにタイトルが入る配列、本文が入る配列、またページ番号が入るint型の箱を準備します。
xcode_todo4

    NSMutableArray *title_array;

    NSMutableArray *sentence_array;
    
    int pageCount;

そして、viewWillAppear内で、それぞれの配列を初期化します。
xcode_todo7

  title_array = [[NSMutableArray alloc] init];

  sentence_array = [[NSMutableArray alloc] init];

最後に、取り出すということを行います。
まず下記を記述してください。
xcode_todo8

 //取り出し
    NSArray* array1 = [ud arrayForKey:@"title"];
    for ( NSString* object in array1 ) {
        [title_array addObject:object];
        
    }
    
    
    NSArray* array2 = [ud arrayForKey:@"sentence"];
    for ( NSString* object in array2 ) {
        [sentence_array addObject:object];
    }

順を追って説明します。
まず、
NSArray* array1 = [ud arrayForKey:@”title”];
は前回titleというキー値で保存した配列をarray1という配列へ取り出しています。
次に

for ( NSString* object in array1 )

は日本語に訳すと、array1の中に入っている物の「数の」回数だけNSString型のobjectという変数名の中にarray1の中に入っているテキスト(この場合はタイトル)をいれてくださいという意味になります。

[title_array addObject:object];

次にobjectというテキストを入れる箱を先ほど初期化したtitle_arrayという配列の後ろに順にいれていきます。
array1の中に入っている物の数の回数だけどんどん入っていくイメージです。

同じように

 NSArray* array2 = [ud arrayForKey:@"sentence"];
    for ( NSString* object in array2 ) {
        [sentence_array addObject:object];
    }

本文も取り出して配列にどんどんいれていきます。

ボタン動作

次に下記のように次へボタンを選択したあと、commandを押しながらドラッグ&ドロップしてプログラムにくっつけます。
xcode_todo10

nextという名前にしました。
xcode_todo11

nextボタンの役割

このnextボタンを押すとページが次々とめくられていき、最後まで来たらはじめに戻すということを行いたいと思います。
次の順で書いていきたいと思います。
まず、ToDoリストになにも登録がない場合、ToDoが登録されていないので登録してくださいとアラートをだします。
次に、ToDoが登録されている数だけ次へを押すと順に表示されていくということを行います。
その中で、最後までToDoが順に表示されたら、はじめに戻すということを行います。

アラートを出す

まず、以下のように記述してください。
xcode_todo12

 if (title_array.count == 0) {
        
        // コントローラを生成
        UIAlertController *ac =
        [UIAlertController alertControllerWithTitle:@"ToDoが登録されていません"
                                            message:@"ToDoを登録してください"
                                     preferredStyle:UIAlertControllerStyleAlert];
        
        //キャンセル用のボタンとアクションを生成
        UIAlertAction *cancel =
        [UIAlertAction actionWithTitle:@"キャンセル"
                                 style:UIAlertActionStyleCancel
                               handler:^(UIAlertAction * action) {
                                   // ボタンタップ時の処理
                                   
                                   
                               }];

        [ac addAction:cancel];

        // アラートを呼び出す
        [self presentViewController:ac animated:YES completion:nil];
    
    }

以前の記事にも書いたように、ここでは条件分岐とUIAlertControllerを使用しています。

まず、

    
if (title_array.count == 0) {

は、ToDoが登録されていない場合、言い換えると「viewWillAppearが呼ばれた際、title_arrayにアプリ内のデータを入れていった時に何も入っていなかったら」という意味になります。
つまりtitle_array.countはtitle_arrayのカウントが0だったらということです。

UIAlertController *ac =
        [UIAlertController alertControllerWithTitle:@"ToDoが登録されていません"
                                            message:@"ToDoを登録してください"
                                     preferredStyle:UIAlertControllerStyleAlert];

でアラート画面を初期化しています。同時にタイトルなどを決めています。

次に

 //キャンセル用のボタンとアクションを生成
        UIAlertAction *cancel =
        [UIAlertAction actionWithTitle:@"キャンセル"
                                 style:UIAlertActionStyleCancel
                               handler:^(UIAlertAction * action) {
                                   // ボタンタップ時の処理
                                   
                                   
                               }];

でボタンが押されたときの処理と、ボタンのタイプを設定しています。
大切な箇所はスタイルをキャンセルボタンにしておくという箇所です。

style:UIAlertActionStyleCancel

そして、ボタンを画面(ac)へはります。

 [ac addAction:cancel];

最後にアラートを表示します。

 // アラートを呼び出す
        [self presentViewController:ac animated:YES completion:nil];

順にToDoを表示していく

次にこちらをみていきましょう。
xcode_todo13

 if (title_array.count>=pageCount){
        
        //常にインクリメント
        pageCount = pageCount + 1;
        
        if (title_array.count<pageCount){
            
            pageCount = 1;
            titleLable.text = [title_array objectAtIndex:0];
            
            toDoTextView.text = [sentence_array objectAtIndex:0];
            
        }else{
        
        titleLable.text = [title_array objectAtIndex:pageCount-1];
        
        toDoTextView.text = [sentence_array objectAtIndex:pageCount-1];
        }
    }

まず、


 if (title_array.count>=pageCount){

は「次へボタンを押していったときに、title_arrayに入っている数のほうが次へボタンを押されたときに順々に増えていくpageCountの数よりも大きかったら」という意味になります。

次に、

  
     pageCount = pageCount + 1;

pageCountの値を1つずつ増やしていきます。これは、取り出す配列の位置を指定するために用います。

その中で

if (title_array.count<pageCount){
            
            pageCount = 1;
            titleLable.text = [title_array objectAtIndex:0];
            
            toDoTextView.text = [sentence_array objectAtIndex:0];
            
        }

は、まず、

if (title_array.count<pageCount)

もしボタンを押したときに次々増加していうpageCountの数が配列(title_array)の数よりも大きくなってしまったら


 pageCount = 1;

pageCountの値を1に戻します。


 titleLable.text = [title_array objectAtIndex:0];
            
 toDoTextView.text = [sentence_array objectAtIndex:0];

さらに、titleLableのテキストをtitle_arrayの0番目(1番はじめの文字)にしてあげます。
ここのobjectAtIndexというのは、配列の番号を指定するときに使います。
toDoTextViewも同様のことを行います。

最後に


else{
        
        titleLable.text = [title_array objectAtIndex:pageCount-1];
        
        toDoTextView.text = [sentence_array objectAtIndex:pageCount-1];
        }

は、そうではなくて、つまり増えていっているときはtitle_arrayの配列の:pageCount番目-1をとりだして、titleLable.textにいれてくださいという処理をしています。
なぜ-1をしているかというと、1回目に次へボタンを押したときにすでにpageCountの値は0から1増えています。なので、-1をしてあげないと、title_arrayの配列の0番目が取得することができないからです。
配列は1から始めるのではなく、0からはじまります。

シミュレーターで確認してみよう

それでは、シミュレーターで確認してみたいと思います。
まず、最初の画面です。
何もToDoが登録されていない状態になります。
右上の+ボタンをおして、ToDoを登録していきます。
xcode_todo14
そして、登録が終わったら次へボタンを押してみてください。
xcode_todo21
次々と変わっていくのがわかり、また循環していっているのがわかるかと思います。
xcode_todo22

最後に

いかがでしたか?

今回はアプリ内に入っている配列を出して、その中身をfor文で次々出していく、また条件分岐をかけて適切な処理を行っていくということを行いました。

これができるようになると、さまざまな場面で応用ができます。例えば、ToDoアプリだけではなく、他のアイディアにそのまま使うことも可能です。次回はToDoが登録された時刻を、同じように表示してみるということを行ってみます。今回は以上です!