チュートリアル:データの追加
このチュートリアルは、二番目のチュートリアル(チュートリアル:ストリーボード) で作成したプロジェクトに基づいています。Foundation での作業、デザインパターンを使用、カスタムクラスを書く で学んだ事を使用して、あなたの ToDoList アプリに動的データのサポートを追加して行きます。
このチュートリアルでは、以下の方法を教えます:
- 共通 Foundation クラスとの作業
- カスタムデータクラスの作成
- データソースとデリゲートプロトコルを実装
- ビューコントローラ間でデータを渡す
このチュートリアルのすべての手順を完了したら、アプリは次のようになるでしょう:
              
データクラスを作成
さあ、Xcode で、既存のプロジェクトを開きましょう。
この時点では、ストーリーボードを使用した ToDoList アプリのためのインターフェイスとナビゲーションスキームが一つずつあります。さあ、データ·ストレージとモデルオブジェクトの動作を追加してみましょう。
アプリの目標は、to-do 項目のリストを作成することですので、最初は、個々の to-do 項目を表現するために、カスタムクラス ToDoItem を作成します。思い出したように、ToDoItem クラスは カスタムクラスを書く で説明しました。
ToDoItemクラスを作成するには
- [ファイル(File)] > [新規(New)] > [ファイル(File)](またはコマンド+ N を押す) を選んで下さい。
- 左側で、iOS の下でソース (Source) を選択します。
- ココアタッチ (Cocoa Touch) クラスを選択し、[次へ(Next)] を選択します。
- クラスフィールドで、ToDoItem を入力します。
- ポップアップメニュー「のサブクラス(Subclass Of)」から NSObject を選択してください。
- [次へ(Next)]をクリックします。
- デフォルトをそのままにしておき、[作成(Create)] をクリックします。
ダイアログが現れ、新しいファイルのテンプレートを選択するように求められます。
デフォルトでは保存の場所がプロジェクト・ディレクトリになっています。
グループオプションのデフォルトはアプリ名、ToDoList になっています。
ターゲットセクションではアプリが選択されており、アプリのテストが選択されていません。
ToDoItem クラスを実装するのは簡単です。その名前、作成日、完了状態のプロパティを持っています。先に行くと ToDoItem クラスのインターフェイスにこれらのプロパティを追加します。
ToDoItem クラスを設定するには
- プロジェクトナビゲータで、ToDoItem.h を選択します。
- インターフェイスに、次のプロパティを追加すると、宣言は次のようになります:
- @interface ToDoItem : NSObject
- @property NSString *itemName;
- @property BOOL completed;
- @property (readonly) NSDate *creationDate;
- @end
チェックポイント:[プロダクト(Product)] > [ビルド(Build)](またはコマンド+ B を押す) を選択してプロジェクトをビルドします。あなたはまだ何も新しいクラスを使用していませんが、コンパイラに何も入力ミスを行っていないことを確認する機会をビルドによって与えます。もし入力ミスをした場合には、コンパイラが提供する警告やエラーを通して読んでそれらを修正してから、すべてがここで説明した通りになるように、このチュートリアルの手順を振り返って下さい。
データをロードする
これで、個々の to-do 項目のデータを作成し、保存することができる、クラスができました。それらの項目のリストを保持する必要もあります。これを追跡する自然な場所は、ToDoListTableViewController クラスであり、ビューコントローラはモデルとビューの間の調整を担当しているので、モデルへの参照を必要としています。
Foundation フレームワークには、項目の追跡リストに適しているクラス、NSMutableArray を含んでいます。配列に項目を追加できるので、可変配列を使用することが重要です。不変のバージョン、NSArray は、それが初期化した後には、それに項目を追加することはできません。
配列を使うには、それを宣言し、作成する両方の必要があります。そのためには、配列を割り当て、初期化しなければなりません。
配列の割り当てと初期化
- プロジェクトナビゲータで、ToDoListTableViewController.m を選択します。
- カスタム・テーブルビュー・コントローラクラス内で Xcode が作成したインターフェイス・カテゴリに次のプロパティを追加します。宣言は次のようになります:
- @interface ToDoListTableViewController ()
- @property NSMutableArray *toDoItems;
- @end
- viewDidLoadメソッドを見つけます。テンプレートの実装は次の通りです。
- - (void)viewDidLoad {
-         [super viewDidLoad];
-         // Uncomment the following line to preserve selection between presentations.
-         // self.clearsSelectionOnViewWillAppear = NO;
-         // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
-         // self.navigationItem.rightBarButtonItem = self.editButtonItem;
- }
- viewDidLoad メソッドでの toDoItems 配列を割り当てと初期化するメソッドの実装を更新します。
- - (void)viewDidLoad {
-         [super viewDidLoad];
-         self.toDoItems = [[NSMutableArray alloc] init];
- }
項目の配列はテーブルビュー・コントローラの実装の詳細なので、.h ファイルの代わりに .m ファイルで宣言します。これは、カスタムクラスを private にします。
このメソッドのテンプレートの実装は、Xcode が ToDoListTableViewController を作成したとき、Xcode によって挿入されたコメントを含んでいます。このようなコードのコメントは、ソースコードファイルに役立つヒントやコンテキスト情報を提供していますが、このチュートリアルではそれらを必要としません。自由にコメントを削除しましょう。
この時点で、項目を追加できる配列になりました。viewDidLoad から呼び出す別のメソッド、loadInitialData でいくつかのデータを追加します。このコードはモジュラータスクのため、独自のメソッドで行え、このメソッドを別にすることで、コードの可読性を向上させることができます。実際のアプリでは、このメソッドは、ファイルなどの永続に保存されたデータをロードします。今のところ、実験のためテストデータを作成しましょう。
配列を作成したら、項目を作成します:割り当てして初期化します。その後、項目に名前を付けます。これはテーブルビューに表示される名前です。いくつかの項目にこれを行ってみましょう。
初期化したデータをロードするには
- ToDoListTableViewController.m で、@implementation 行以下に loadInitialData と呼ばれる新しいメソッドを追加します。
- - (void)loadInitialData {
- }
- このメソッドでは、いくつかのリスト項目を作成し、配列に追加します。
- - (void)loadInitialData {
-         ToDoItem *item1 = [[ToDoItem alloc] init];
-         item1.itemName = @"Buy milk";
-         [self.toDoItems addObject:item1];
-         ToDoItem *item2 = [[ToDoItem alloc] init];
-         item2.itemName = @"Buy eggs";
-         [self.toDoItems addObject:item2];
-         ToDoItem *item3 = [[ToDoItem alloc] init];
-         item3.itemName = @"Read a book";
-         [self.toDoItems addObject:item3];
- }
- loadInitialData メソッドを呼び出すように viewDidLoad メソッドの実装を更新します。
- - (void)viewDidLoad {
-         [super viewDidLoad];
-         self.toDoItems = [[NSMutableArray alloc] init];
-         [self loadInitialData];
- }
チェックポイント:[プロダクト(Product)] > [ビルド(Build)] を選択してプロジェクトをビルドします。 loadInitialData メソッドに対して多数のエラー行が表示されるはずです。間違っている鍵は最初の行で、ちゃんと言うと「宣言されていない識別子 ToDoItem の使用」です。これは、ToDoListTableViewController をコンパイルしているときにコンパイラが ToDoItem クラスについて知らないことを意味します。コンパイラは非常に特殊であり、何に注意を払うべきかを明示的に指示する必要があります。
ToDoItem クラスに注意を払うようにコンパイラに指示するには
- ToDoListTableViewController.m ファイルの先頭近くで、#import"ToDoListTableViewController.h の行を見つけます。
- そのすぐ下に次の行を追加します。
#import "ToDoItem.h"
チェックポイント:[プロダクト(Product)] > [ビルド(Build)] を選択してプロジェクトをビルドします。これで、エラーなしでビルドできるでしょう。
データの表示
この時点で、カスタム・テーブルビュー・コントローラのサブクラス、ToDoListTableViewController は、いくつかのサンプルの to-do 項目があらかじめ設定された可変配列を持っています。今、あなたは、このクラスで管理される、テーブルビューのデータを表示する必要があります。
動的なデータを表示するには、テーブルビューは、2つの重要なヘルパーを必要とします:データソースとデリゲートです。テーブルビューの データソース は、その名前によって示唆されるように、それが表示するために必要なデータを持つテーブルのビューを提供します。テーブルビューの デリゲート は、テーブルビューが、セル選択、行の高さ、およびデータの表示に関連する他の側面を管理するのを補助します。デフォルトでは、UITableViewController とその任意のサブクラスは、それに関連するテーブルビューのデータ・ソース (UITableViewDataSource プロトコル) とデリゲート (UITableViewDelegate プロトコル) 両方をテーブルビュー・コントローラに作るために必要なプロトコルを採用しています。あなたの仕事は、あなたのテーブルビューが正しい動作を持つようにテーブルビュー・コントローラのサブクラスで適切なプロトコル・メソッドを実装することです。
機能しているテーブルビューには、3つのテーブルビューのデータソース・メソッドが必要です。これらのうち最初は、多くのセクションをどのように表示するか、テーブルビューに伝える numberOfSectionsInTableView: です。セクションは、視覚的にテーブルビュー、特に多くのデータがあるテーブルビュー内のセルをグループ化する方法を提供します。ToDoList アプリ内の物のような単純なテーブルビューのように、単一のセクションを表示する必要があり、 numberOfSectionsInTableView データソース・メソッドの実装は簡単です。
テーブルビュー内にセクションを表示するには
- プロジェクトナビゲータで、ToDoListTableViewController.m を選択します。
- numberOfSectionsInTableView: データソースメソッドを検索します。テンプレートの実装は次のようになります。
- - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- #warning Potentially incomplete method implementation.
-           // Return the number of sections.
-           return 0;
- }
- 1つのセクションが欲しいので、戻り値を 0 から 1 へ変更し、警告の行を削除します。
- - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
-           // Return the number of sections.
-           return 1;
- }
- 実装が完了したので、「潜在的に不完全なメソッドの実装」という警告行を削除しました。
次のデータソース・メソッド、tableView:numberOfRowsInSection: は、与えられたセクションにどれだけ行数を表示するかをテーブル・ビューに指示します。あなたのテーブルには一つのセクションがあり、各 to-do 項目は、セクションに独自の行を持っています。つまり、行数はあなたの toDoItems 配列内の ToDoItem オブジェクトの数でなければならないことを意味します。
テーブル・ビュー内の行数を返すには
- ToDoListTableViewController.m で、tableView:numberOfRowsInSection: データソース・メソッドを見つけます。テンプレートの実装は次のようになります。
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- #warning Incomplete method implementation.
-           // Return the number of rows in the section.
-           return 0;
- }
- tableView:numberOfRowsInSection: データソースメソッドを行の適切な数を返すように変更し、警告行を削除します。
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
-           // Return the number of rows in the section.
-           return [self.toDoItems count];
- }
持っている to-do 項目の数を返すようにしたいとします。幸いなことに、 NSArray の配列内の項目数を返す、count と呼ばれる便利なメソッドがあり、行の数は [self.toDoItems count] となります。
最後のデータソース・メソッド、 tableView:cellForRowAtIndexPath: は、指定した行を表示するようにセルに要求します。テーブルビューの各行は、1つのセルがあり、そのセルは、そのコンテンツがどのようにレイアウトされ、その行に表示されているかを決定します。
これまでは、コードだけで取り組んできましたが、行に表示するためのセルは、インターフェイスに非常に多くの部分を依存します。幸いなことに、Xcode は簡単にストーリーボードでカスタム・セルをデザインすることを簡単にします。最初のタスクは、静的なコンテンツを使用するのではなく、動的なコンテンツとプロトタイプのセルを使用している事をテーブルビューを伝えることです。
テーブルビューにプロトタイプ・セルを使用するよう構成するには
- プロジェクトナビゲータで、Main.storyboard を選択します。
- アウトラインビューで、To-Do リストシーンの下にテーブルビューを選択します。
- テーブルビューを選択した状態で、属性インスペクタ をユーティリティエリアで開きます。
- 属性インスペクタで、テーブルビューの[コンテンツ(Content)] フィールドを静的セル(Static Cell) からダイナミックプロトタイプ(Dynamic Prototype)へ変更します。
インターフェースビルダーは、あなたが設定した静的セルを取り去り、それらのすべてをプロトタイプに変換します。
プロトタイプ・セルは名前が示すように、テキストスタイル、色、画像、またはその他の属性でそれらを表示したいように構成されているセルであるが、実行時にテーブルビュー・データソースからデータを取得するものです。データソースは、行ごとにプロトタイプ・セルをロードし、その行のデータを表示するように、そのセルを構成します。
正しいセルをロードするには、データソースはそれが何と呼ばれているかを知る必要があり、その名前もストーリーボードに設定しなければなりません。
あなたがプロトタイプ・セルの名前を設定している間、ユーザーがそれをタップしたときの、セルの外観はどうあるか決定する別のプロパティ、セル選択のスタイルも設定します。ユーザーがそれをタップしてもセルがハイライト表示されないようにするには、そのようにセル選択のスタイルを設定します。これは、to-do リストでユーザーがタップした時に、完了したか完了しないかマークする事ーこのチュートリアルの後半で実装しますーは、セルが持っていて欲しい動作です。
プロトタイプ・セルを構成するには
- テーブル・ビューの最初のテーブル・ビュー・セルを選択します。
- 属性インスペクタで、識別子フィールド内で ListPrototypeCell と入力します。
- 属性インスペクタで、セルの選択フィールドを、[デフォルト(Default)] から [なし(None)] に変更します。
- アウトラインビューで、最初のもの以外のすべてのセルを選択して削除します。
この時点から1つのプロトタイプ・セルが必要です。
また、フォントやプロトタイプ・セルの他の属性を変更できます。基本的な構成は簡単ですので、それをしておきましょう。
次のステップは、どのように tableView:cellForRowAtIndexPath: を実装することによって、与えられた行のセルを構成するかをデータソースに教えることです。少数の行を持つテーブル・ビューの場合、すべての行が一度に画面に表示される場合があり、そのため、このメソッドは、表(テーブル)の行ごとに呼び出されます。しかし、多数の行を持つテーブル・ビューは、特定の時間では、総項目のごく一部しか表示できません。テーブルビューにとって最も効率的なのは、表示されている行にセルを尋ね、 tableView:cellForRowAtIndexPath: が テーブルビューに何を行うように許可する事だけです。
テーブル・ビュー内の与えられた行について、toDoItems 配列内の対応する項目を取得しセルを構成し、その項目の名前をそのセルのテキスト・ラベルに設定してください。
テーブル・ビュー内のセルを表示するには
- プロジェクトナビゲータで、ToDoListViewController.m を選択します。
- tableView:cellForRowAtIndexPath: データソースメソッドを検索し、コメントしてあるメソッドから /* と */ を削除します。
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
-         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier" forIndexPath:indexPath];
-         // Configure the cell...
-         return cell;
- }
- ストーリーボードに設定したプレースホルダー識別子を識別子に変更します。 タイプミスを防ぐために、ストーリーボードからコピーし、実装ファイルにペーストします。セルを返すコード行は次のようになります。
- return 文の直前に、次のコード行を追加します。
- ToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
- cell.textLabel.text = toDoItem.itemName;
それを実行した後、テンプレートの実装は次のようになります。
テンプレートは、いくつかのタスクを実行します。これは、テーブル・ビューをセルからプレースホルダー識別子で尋ね、セルを構成するためのコードがどこに行くかについてのコメントを追加し、セルを返します。
あなたのアプリでこのコードを機能させるためには、ストーリーボードに設定した一つにプレースホルダー識別子を変更してから、セルを構成するためのコードを追加する必要があります。
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];
これらの行は toDoItems 配列内の適切な項目を取得し、その項目の名前を設定してセルに表示します。
あなたの tableView:cellForRowAtIndexPath: メソッドは次のようになります。
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
-         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];
-         ToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
-         cell.textLabel.text = toDoItem.itemName;
-         return cell;
- }
チェックポイント:あなたのアプリを実行します。あなたが loadInitialData に追加した項目のリストがテーブル・ビューのセルとして表示されるでしょう。
完了状態項目をトグル
to-do リストは、項目を完了としてマークをした事がなければ、あまり良くありません。さて、そのためのサポートを追加します。テーブル・ビューは、シンプルなインターフェイスを実装させ、ユーザーがセルをタップした時に項目の完了状態を切り替えます。特に、テーブルビューはそのデリゲートに、ユーザーがセルをタップしたときに通知します。デリゲートがこの通知を受け取った時に、あなたはそれに反応する2つの方法のうち一つのコードを書きます。
- そのセル内の項目が完了していない場合に、それを完了したとコードはマークし、項目の隣にチェックマークで完成した項目が表示します。
- そのセル内の項目が完了している場合、コードは未完了としてそれをマークし、その隣のチェックマークを削除します。
あなたがしなければならないのは、 tableView:didSelectRowAtIndexPath: デリゲートメソッドを実装し、ユーザーのタップに対応し、それに応じてあなたの to-do リストの項目を更新する事です。
完了・未完了の項目をトグルするには
- プロジェクトナビゲータで、ToDoListTableViewController.m を選択します。
- @end ラインのすぐ上、ファイルの最後に以下の行を追加します。
- #pragma mark - Table view delegate
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- }
- このメソッドでは、タップに反応するが、実際には選択したセルをそのままにしないようにしたいとします。選択した直後にセルの選択を解除するには、次のコードを追加します。
- タップされたセルに対応する toDoItems 配列内の ToDoItem を検索するには、このコード行を追加します。
- タップされた項目の完了状態を切り替えます。
- あなたが今更新したデータの行を再ロードするようにテーブルビューに通知します。
コピー&ペーストせず、2行目をタイプして入力してみてください。Xcode の素晴らしい時間節約機能の一つ、コード補完 を見ることができます。Xcode は、潜在的な補完リストを表示する能力があり、欲しいものを見つけたら Return キーを押すまで、リストをスクロールできます。Xcode はあなたの代わりに、行全体を挿入します。
[tableView deselectRowAtIndexPath:indexPath animated:NO];
ToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
tappedItem.completed = !tappedItem.completed;
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
tableView:didSelectRowAtIndexPath: メソッドは次のようになります。
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-         [tableView deselectRowAtIndexPath:indexPath animated:NO];
- ToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
-         tappedItem.completed = !tappedItem.completed;
-         [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
- }
チェックポイント:あなたのアプリを実行します。loadInitialData に追加した項目のリストがテーブル・ビューのセルとして表示されます。しかし項目をタップしても、何も起きていないようにみえます。なぜでしょうか?
その理由は、項目の完了状態を表示するためにテーブルビューのセルを構成していなかったということです。その方法は、tableView:cellForRowAtIndexPath: データソース・メソッドに戻り、項目が完了したときにチェックマークを表示するようにセルを構成します。
テーブルビューセルは右側にセルのアクセサリーを持つことができます。デフォルトでは、アクセサリーは選択されていませんが、しかし、チェックマークを表示してセルを変更できます。
項目の完了状態を表示するには
- tableView:cellForRowAtIndexPath:メソッドの所に行きます。
- セルのテキストラベルを設定する行のすぐ下に次のコードを追加します。
- if (toDoItem.completed) {
-         cell.accessoryType = UITableViewCellAccessoryCheckmark;
- } else {
-         cell.accessoryType = UITableViewCellAccessoryNone;
- }
このコードは、to-do 項目の完了状態を確認し、それに基づいて、セルのアクセサリを設定します。
tableView:cellForRowAtIndexPath: メソッドは次のようになります。
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];
-         ToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
-         cell.textLabel.text = toDoItem.itemName;
-         if (toDoItem.completed) {
-                 cell.accessoryType = UITableViewCellAccessoryCheckmark;
-         } else {
-                 cell.accessoryType = UITableViewCellAccessoryNone;
-         }
-         return cell;
- }
チェックポイント:あなたのアプリを実行します。loadInitialData に追加した項目のリストがテーブルビューのセルとして表示されます。項目をタップすると、チェックマークがその隣に表示されます。再度同じ項目をタップすると、チェックマークが消えます。
新しい項目を追加
To-Do リストアプリの機能を作成するための最後のステップは、項目を追加する機能を実装する事です。ユーザーが add-to-do-item シーンのテキストフィールドに項目名を入力し、[保存(Save)] ボタンをタップすると、 AddToDoItemViewController に新しい to-do 項目の作成と、to-do リスト内に表示するように ToDoListTableViewController にそれを戻します。
まず、構成する to-do 項目を持っている必要があります。AddToDoItemViewController に、新しい to-do 項目に関する情報を保持するプロパティを与えます。
AddToDoItemViewController に to-do 項目を追加するには
- プロジェクトナビゲータで、AddToDoItemViewController.h を選択します。
- AddToDoItemViewController.h で、#import <UIKit/UIKit.h> 行のすぐ下に ToDoItem.h への import 宣言を追加します。
- インターフェイスに toDoItem プロパティを追加します。
後でテーブルビュー・コントローラから to-do 項目にアクセスする必要があるので、このプロパティを public にすることが重要です。それが実装ファイル、AddToDoItemViewController.m の代わりに、インターフェイスファイル、AddToDoItemViewController.h で宣言する理由です。
#import "ToDoItem.h"
- @interface AddToDoItemViewController : UIViewController
- @property ToDoItem *toDoItem;
- @end
新しい項目の名前を取得するには、AddToDoItemViewController は、ユーザーが名前を入力したテキストフィールドの内容にアクセスする必要があります。これを行うには、ストーリーボードのテキストフィールドに AddToDoItemViewController.m からの接続を作成します。
AddToDoItemViewController コードにテキストフィールドを接続するには
- プロジェクトナビゲータで、Main.storyboard を選択します。
- アウトラインビューで、Add To^Do 項目シーンの下で Add To-Do 項目を選択します。
- Xcode のツールバーのアシスタントボタンをクリックしてアシスタントエディタを開いてください。
- アシスタントエディターを変更して[プレビュー(Preview)] から[自動(Automatic)] > AddToDoItemViewController.m にします。
- ストーリーボードで、テキストフィールドを選択します。
- キャンバス上のテキストフィールドから Control-ドラッグして、右側のエディタで表示されるコードに至り、 AddToDoItemViewController.m で @interface 行のちょうど下の行でドラッグを停止します。
- 表示されるダイアログで、名前を textfield と入力します。
- [接続]をクリックします。
オプションの残りは、そのままにしておきます。あなたのダイアログは次のようになります。
Xcode は、テキストフィールドへのポインタを格納する AddToDoItemViewController.m に必要なコードを追加し、その接続を設定するためにストーリーボードを構成します。
さらに、AddToDoItemViewController は新しい to-do 項目をいつ作成するかを知っておく必要があります。あなたは[保存(Save)]ボタンがタップされた場合にのみ項目を作成することにしたいです。これがいつ発生したかを決定できるように、AddToDoItemViewController.m で[保存]ボタンをアウトレットとして追加します。
AddToDoItemViewController コードに保存ボタンを接続するには
- ストーリーボードで、[保存] ボタンを選択してください。
- 右に表示されるエディタでコードに、キャンバス上の[保存]ボタンから Control- ドラッグし、 AddToDoItemViewController.m の textField プロパティのすぐ下の行でドラッグを止めます。
- 表示されるダイアログで、[名前(Name)]に、saveButton と入力します。
- [接続(Connect)]をクリックします。
オプションの残りはそのままにしておきます。ダイアログは次のようになります。
これで、[保存]ボタンを識別するための方法が出来ました。[保存] ボタンがタップされた時項目を作成するようにしたいので、それが発生したのはいつか、知る必要があります。
ユーザーが[キャンセル]や[保存]ボタンをタップしたとき、巻き戻しセグエで、to-do リストへと戻って行く、つまり2番目のチュートリアルで構成したものを始める事を思い出して下さい。セグエが実行される前に、システムが prepareForSegue: を呼び出して準備する機会を含むビューコントローラーを提供します。これはまさにビューコントローラが、ボタンのうちいずれかをタップしたのかを確認して、それは[保存]ボタンであった場合、新しい to-do 項目を作成すべき時点です。([キャンセル]ボタンをタップした場合は、項目を保存するために何かをする必要はありません。)
ユーザーが[保存]ボタンをタップしたときにのみ項目を作成するように AddToDoItemViewController に伝えるために
- プロジェクトナビゲータで AddToDoItemViewController.m を選択します。
- 検索して prepareForSegue: データソースメソッドのコメントを解除します。(囲む /* */ の文字を削除して、メソッドのコメントを解除します。)
- #pragma mark - Navigation
- // In a storyboard-based application, you will often want to do a little preparation before navigation
- - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
-         // Get the new view controller using [segue destinationViewController].
-         // Pass the selected object to the new view controller.
- }
- このメソッドでは、[保存(Save)]ボタンがタップされたかどうかを確認します。
- テキストがテキストフィールドに入力されたかどうかを参照するには、このコードを追加して下さい。
- if (self.textField.text.length > 0) {
- }
- テキストがあれば、新しい項目を作成して、テキストフィールドのテキストの名前を付けます。また、完了状態が NOfalse に設定されていることを確認します。
- self.toDoItem = [[ToDoItem alloc] init];
- self.toDoItem.itemName = self.textField.text;
- self.toDoItem.completed = NO;
アシスタントエディターがまだ開いている場合は、Xcode のツールバーの[標準(Standard)]ボタンをクリックして、標準のエディタに戻ります。
それを実行した後、テンプレートの実装は次のようになります。
[保存]ボタンをタップしていない場合は、項目を保存する代わりに、他に何もせずにメソッドに返して欲しいです:
if (sender != self.saveButton) return;
テキストがない場合、項目を保存したくないので、他に何もコードを追加する必要はありません。
prepareForSegue: メソッドは次のようになります。
- - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
-         if (sender != self.saveButton) return;
-         if (self.textField.text.length > 0) {
-                 self.toDoItem = [[ToDoItem alloc] init];
-                 self.toDoItem.itemName = self.textField.text;
-                 self.toDoItem.completed = NO;
-         }
- }
新しい項目は、to-do リストに項目を追加できるように ToDoListTableViewController に戻して渡す必要があります。これを実現するために、第二のチュートリアルで書いた、unwindToList を再訪する必要があります。このメソッドは、後に巻き戻しセグエトリガと呼ばれます。
unwindToList: メソッドは、巻き戻しセグエのターゲットとして使用されるすべてのメソッドのように、パラメータとしてセグエを使います。セグエパラメータは、 ToDoListTableViewController に AddToDoItemViewController から巻き戻すセグエです。セグエは2つのビューコントローラ間の移行なので、そのソースであるビューコントローラー AddToDoItemViewController を認識しています。セグエオブジェクトに、そのソースであるビュー·コントローラを尋ねることによって、 unwindToList: メソッドのソース・ビューコントローラに保管されているどんなデータにもアクセスできます。この場合では、 toDoItem プロパティにアクセスしたいわけです。それがもし nil なら、項目は決して作成されません - テキストフィールドにテキストが入力されなかったか、ユーザーが[キャンセル]ボタンをタップした時には。toDoItem の値があれば、項目を取り戻し、toDoItems 配列に追加し、テーブルビューにデータを再ロードすることにより、to-do リストに表示します。
新しい項目を格納して表示するには
- プロジェクトナビゲータで、ToDoListTableViewController.m を選択します。
- ToDoListTableViewController.m で、#import"ToDoItem.h" 行のすぐ下に AddToDoItemViewController.h の import 宣言を追加します。
- 2番目のチュートリアルで追加した unwindToList: メソッドを見つけます。それは次のようです:
- - (IBAction)unwindToList:(UIStoryboardSegue *)segue {
- }
- このメソッドでは、AddToDoItemViewController から巻き戻したコントローラのソース·ビュー·コントローラーを回収します。
- コントローラの toDoItem プロパティの値を取得します。
- 項目が存在するかどうかを確認するために、以下のコードを追加します。
- if (item != nil) {
- }
- テーブル内のデータを再ロードします。
#import "AddToDoItemViewController.h"
AddToDoItemViewController *source = [segue sourceViewController];
ToDoItem *item = source.toDoItem;
ユーザーが add-to-do-item シーンのテキストフィールドにテキストを入力して、[保存(Save)]ボタンをタップした場合、toDoItem プロパティの値は、項目を含みます。ユーザーがテキストフィールドにテキストを入力しなかったか、画面を閉じるのに、[キャンセル(Cancel)]ボタンをタップした場合は、toDoItem プロパティの値は、項目を含みません。あなたは項目が存在するかどうかを確認する必要があります。
それが nil の場合は、toDoItem プロパティには項目がないので、項目を保存するために追加作業を行う必要はありません。
それが存在する場合には、 toDoItems 配列に、以下のように項目を追加します。
[self.toDoItems addObject:item];
テーブルビューがデータを追跡していないため、表示すべき新しいデータがあるとき、テーブルビューに通知するのは、この場合テーブルビューコントローラであるデータソースの責任です。
[self.tableView reloadData];
unwindToList:メソッドは次のようになります。
- - (IBAction)unwindToList:(UIStoryboardSegue *)segue {
-         AddToDoItemViewController *source = [segue sourceViewController];
- ToDoItem *item = source.toDoItem;
-         if (item != nil) {
-                 [self.toDoItems addObject:item];
-                 [self.tableView reloadData];
-         }
- }
チェックポイント:あなたのアプリを実行します。[追加(Add)]ボタン (+) をクリックして新しい項目を作成すると、to-do リストはそれを示すでしょう。おめでとう!あなたは、ユーザからの入力を受け取り、オブジェクトに格納し、2つのビューコントローラ間で、そのオブジェクトを渡すアプリを作成しました。これは、ストーリーボードに基づいたアプリでの、シーン間のデータ移動の基礎(foundation)となっています。
総括
iOS 用アプリの開発のこの入門ツアーはこれで完了です。最後のセクションでは、ドキュメントの周りの道を見つける方法についてのより多くの情報を与え、それはあなたが、より高度なアプリを作成する方法を学ぶように、進むいくつかの次のステップを示唆します。
前の章:カスタムクラスの作成
次の章:iOS テクノロジー