今回から一歩進んだ内容に踏み込みます。まずは、より高度なアクションを行うための開発方法を開設します。
アクションとアウトレットの追加
今回はより高度なアクションについて学んでいきます。そのため、追加アクションの作成や、アクションの中でパーツにアクセスするためのアウトレットの作成が頻繁に行われます。
これまで、アクションとアウトレットはInterface Builderの中でFile's Ownerに作成し、Write Class Filesからプログラムのファイルに書き出していました。しかしながら、途中でアクションやアウトレットを追加したい場合、この方法では既に書いてあるアクションが全て消えてしまい、最初からやり直しということになってしまいます(※ ) 。
そこでまず今回は、Interface Builderを使わずに、File's Ownerにアウトレットやアクションを追加する方法を学びます。
テンプレートの作成
テスト用にプロジェクトを作成し、アウトレットやアクションがひとつずつある状態にします。UITestというプロジェクトを作成してみましょう。
プロジェクトを作成したら UITestViewController.xib を開き、File's Ownerにアウトレットとアクションをそれぞれ1つずつ作成します。
File's Ownerに作成したアウトレットとアクション
File's Ownerが選択された状態でInterface Builderのメニューから「Write Class Files...」を選択し、Replaceを選択してファイルを書き出します。これで、アウトレットとアクションがプログラムのファイルに反映されました。
アウトレットの追加
アウトレットの追加はとても簡単です。アウトレットは、Viewコントローラのヘッダファイルに列挙されています。ここでは UITestViewController.h がそれに該当します。
UITestViewController.h に列挙されたアウトレットとアクション
ファイルの中身を見てみると、上のほうにアウトレットが、下のほうにアクションが並んでいるのがわかります。この上のほうにあるアウトレットを増やしていけば良いだけです。アウトレットの名前は変える必要がありますので myOutlet2とmyOutlet3という名前で2つ追加してみましょう。
アウトレットを2つ追加
追加したらファイルを保存してください。Xcodeのメニューにある「ファイル」 →「 保存」か、コマンド+Sで保存します。保存したら、Interface Builderに戻り File's Owner のアウトレットを確認してみましょう。追加したアウトレットが増えているのがわかります。
追加されたアウトレット
このように、Xcode側で変更したファイルの情報は、Interface Builder側に即座に反映されるようになっています。
アクションの追加
アクションの追加もアウトレットと同様に、UITestViewController.hを編集して行います。下のほうにあるアクションを、myAction2、myAction3という名前で2つ増やしてみましょう。
アクションを2つ追加
ファイルを保存し、Interface BuilderでFile's Ownerを確認します。
追加されたアクション
アクションの場合は、もう1つ作業が必要です。実際のアクションを記述する UITestViewController.m にもこれを追加する必要があります。
UITestViewController.mに記述されたアクション
アクションは、アクション名の書かれている行から始まり、アクションの中身を記述したプログラムがあって、中括弧で閉じられているところで終わります。この部分に習って、myAction2とmyAction3を追加してみましょう。
アクションを記述する側の追加
あとはいつも通りアクションをパーツに接続し、アクションの中身を記述すれば良いだけです。
画面を覆うキーボード
テキストフィールドをタッチすると現れるキーボードは、それだけで画面の半分を覆ってしまいます。そのため、テキストフィールドは画面の上半分、キーボードによって隠れない場所に配置するのが基本です。もし下の方にもテキスト入力パーツを配置したい場合は、テキストフィールドを上半分に配置したViewを別に作り、それを下半分に配置されたパーツから呼び出して使うのが一般的です。つまり、キーボード入力を行うための専用のViewを用意してあげるというわけです。ここでは説明しませんが、複数のViewを扱えるようになった暁にはこの方法が役に立つことでしょう。
画面の下半分を覆ってしまうキーボード
では、テキストフィールドは画面の上半分に配置したとして、キーボードによって覆われてしまった下半分は全く使えないかというと、そうではありません。ユーザがキーボードからの入力を完了したら、キーボードを消してあげれば良いのです。
基本的なキーボードの消し方
キーボードの消し方は簡単です。任意のアクションを1つ作成します。作成したアクションはテキストフィールドの「Did End On Exit」に接続します。
アクションはDid End On Exitに接続
たったこれだけで、キーボードの一番右下にあるボタンを押すことでキーボードが消えるようになりました。この右下のボタンは最初「return」や「改行」になっていますが、この表示はいくつかの中から選択することができます。テキストフィールドが選択された状態で、インスペクタのAttributesタブから「Return Key」の項目を変更してみましょう。入力完了をあらわす「Done」などを設定しておくとわかりやすいでしょう。
Return Keyの設定
キーボードが消えると同時に、設定したアクションが呼び出されます。
キーボードの右下のボタンを押すとアクションが実行される
キーボードを閉じるためのボタン
ところが、これをバーゲン教師で適用しようとすると大きな問題が出てきます。キーボードの種類を「Number Pad」や「Phone Pad」にした場合、右下のボタンはバックスペースキーとなってしまい、入力完了を受け取るきっかけとなるボタンが存在しないのです。
入力完了ボタンがないキーボードの例
そこでこれらの入力完了ボタンがないキーボードを用いる場合には、自前で入力完了ボタンを用意しておく必要があります。
Viewにラウンドレクトボタンを配置してそれを入力完了ボタンに設定してみましょう。ラウンドレクトボタンをキーボードに覆われない場所に配置して、Touch Downにアクションを割り当てます。
入力完了ボタンの設置とアクションの接続
入力完了の合図は、テキストフィールドに接続されたアウトレットoutlet(もしくは任意のアウトレット名)に対して次のメッセージを実行することで通知されます。
[outlet endEditing:YES];
これで、キーボードの完了ボタンに相当するものができました。実際には入力を強制終了しているので、テキストフィールド側にアクションの設定は必要なく、ボタンを押すと無条件でキーボードが消えてくれます。ビルドして動作を確かめてみましょう。
入力完了ボタンを押すとキーボードが消える
ボタンをキーボードに連動させる
この入力完了ボタンは、キーボードからの文字入力中だけ表示されていれば良いものです。キーボードが表示されていないときにこのボタンが押されても、とくに何も起きませんが、常に表示されていると紛らわしいので、キーボードが表示されている間だけこのボタンを表示するようにしてみましょう。
まずは、ボタンを出したり消したりするメッセージを実行するために、ボタンに対するアウトレットを作成します。これをラウンドレクトボタンに接続し、プログラムからはアウトレットを通してボタンの表示、非表示を切り替えます。
ボタンを表示するのはテキストフィールドの入力が開始された時ですので、入力開始時に呼び出されるアクションを設定します。アクションを作成し、テキストフィールドの「Editing Did Begin」に接続します。
アクションの中では、ボタンに接続されたアウトレット outlet(もしくは任意のアウトレット名)に対して次のメッセージを実行します。
[outlet setHidden:NO];
これで入力開始時にボタンが表示されるようになりました。次は入力完了時にボタンを消す処理です。ボタンが押された際のアクションはすでに作成していますので、その中で次のメッセージを実行します。
[outlet setHidden:YES];
これで入力完了時にボタンが消えるようになりました。
さらにアプリ起動時はこのボタンは消しておきたいので、インスペクタを使ってボタンの初期状態を非表示にしておきます。ラウンドレクトボタンが選択されている状態で、インスペクタの「Drawing」から「Hidden」を選択します。
Xcodeに戻り、ビルドして進行をクリックして動作を確かめてみましょう。
透明ボタンを使ったテクニック
さらに進んだテクニックとして、パーツ以外のどこをタッチしてもキーボードが閉じるようにする、ということもできます。入力完了ボタンを配置する場所がないような場合に役に立ちます。
ラウンドレクトボタンはインスペクタの「Type」からいくつかの形状から選択することができますが、ここで「Custom」を選択するとボタン全体を透明化することができます。
ボタンの種類を設定
Customを選択し、透明になったボタンがViewの上半分を覆うように配置します。
画面の上半分を透明ボタンで覆う
もうおわかりでしょう。この透明ボタンが押されたことをアクションで受け取って、キーボードを閉じる処理を行えば良いわけです。ここで注意しなければならないのは、見た目は透明でも普通のボタンですので、他のパーツの上にかぶさってしまうと下のパーツがタッチできなくなってしまいます。そこで、この透明ボタンは重ね順で常に他のパーツよりも一番下(背面)に配置しておく必要があります。ボタンが選択された状態でInterface Builderのメニューから「Layout」 →「 Send Backward」を選ぶと最背面に送ることができます。
「Layout」 →「 Send Backward」で最背面に送る
あとは通常のラウンドレクトボタン同様、アクションを接続してキーボードを閉じるためのメッセージを記述してみましょう。
確認ダイアログ
ユーザの操作が本当に正しいかどうか、操作の進行を確認するためにダイアログを表示したい場合があります。そこで登場するのが UIActionSheet と呼ばれるものです。
UIActionSheetを使った確認ダイアログ
デリゲートの設定
ユーザの操作はアクションを通じて受け取るのが基本です。これまで作成したアクションはすべてInterface Builderを使ってパーツに接続していました。しかしUIActionSheetの作成にはInterface Builderを使いません。そのため、ユーザがダイアログのボタンを押したことを受け取るために「デリゲート」と呼ばれる仕組みを使います。デリゲートの仕組みはObjective-Cを初めたての方には難しい内容ですので、プログラムに慣れてきてから書籍などで学ぶことをおすすめします。
なにはともあれ、早速デリゲートと呼ばれるものを設定してみましょう。UITestViewController.hの @interface から始まる行を、次のように変更します。
@interface UITestViewController : UIViewController <UIActionSheetDelegate> {
このように <UIActionSheetDelegate> を追加することで、UIActionSheetにおけるユーザの操作、すなわちダイアログのボタンを押したということをUIViewControllerの中で受け取ることができるようになります。
ダイアログの表示
ダイアログを表示するために UITestViewController.m を編集します。ここではmyAction1をダイアログが表示されるきっかけのアクションとします。
- (IBAction)myAction1:(id)sender
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"○○しますか?" delegate:self cancelButtonTitle:@"キャンセル" destructiveButtonTitle:@"○○する" otherButtonTitles:nil];
[actionSheet showInView:self.view];
[actionSheet release];
}
これでmyAction1が実行された時にダイアログが表示されます。myAction1をラウンドレクトボタンに接続したり、それぞれの文字の部分をいろいろ変更したりして試してみて下さい。
ユーザ操作の受け取り
ダイアログのボタンが押されたことを受け取るためには、次のプログラムをUITestViewController.mに記述します。
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0){
// destructiveButtonが押された場合の処理
}
}
destructiveButtonはダイアログの赤いほうのボタンです。ユーザが入力したデータをリセットするなどの処理をここに記述します。
データの保存
テキストフィールドに入力された文字や、プログラム内で扱うデータは、アプリを終了するとすべて消えてしまいます。これを保持しておき、次回アプリ起動時に書き戻したい場合のテクニックを紹介します。
ここで紹介する方法はあくまで簡易的なものです。大量のデータを保持したり、データベースのように扱うのには向いていませんので、あくまで「一時的に文字や数字をとっておきたい」という場合にのみ使用して下さい。
データの保存は、次のメッセージを実行します。
[[NSUserDefaults standardUserDefaults] setObject:myObj forKey:@"myKey"];
myObjに保存したいデータを、myKeyの部分はそれを識別するための任意の文字列(キー)を設定します。
保存したデータを取り出すためには、次のメッセージを実行します。
[[NSUserDefaults standardUserDefaults] valueForKey:@"myKey"];
myKeyで指定したキーで保存されているデータが、myObjに格納されます。
これだけでは少しわかりづらいので、実践してみましょう。
保存と取り出しボタン
ラウンドレクトボタンを2つ配置し、それぞれのTouch Downをアクションに接続します。テキストフィールドとラベルにはそれぞれアウトレットを作成して接続しておきます。ここではテキストフィールドのアウトレットを「hozonmoto」 、ラベルのアウトレットを「toridashisaki」とし、保存のアクションを「myAction1」に、取り出しのアクションを「myAction2」に接続しました。
- (IBAction)myAction1:(id)sender {
[[NSUserDefaults standardUserDefaults] setObject:[hozonmoto text] forKey:@"test"];
}
- (IBAction)myAction2:(id)sender {
[toridashisaki setText:[[NSUserDefaults standardUserDefaults]valueForKey:@"test"]];
}
myAction1では、hozonmotoに入力された文字列を取得して、testというキーで保存しています。myAction2では、testというキーで保存されているデータを取り出して、toridashisakiに設定しています。
ビルドして動作を試してみましょう。テキストフィールドに文字を入力し「保存」を押します。その後「取り出し」を押すと保存した文字がラベルに表示されます。
文字列を保存して取り出す
ここでアプリを一旦終了し、再度起動して「取り出し」を押すと、先ほど保存した文字列がラベルに表示されます。
アプリの公開を目指して
今回学んだ内容は、前回 ご紹介した「バーゲン教師」と「割勘奉行」の機能を満たすために不足している部分を補ったものです。次回はこの2つのアプリの動きについて、実際のプログラムを見ながら、これまでに学習した内容を復習していきます。その後はいよいよApp Storeへの登録です。これまでに学んだことを活かし、App Storeで公開するためのアプリ作りにぜひチャレンジしましょう。