ナンバーパッドに Enter ボタンを表示する : Objective-C プログラミング
PROGRAM
UIKeyboard のナンバーパッドに Enter ボタンを表示する
UITextField の keyboard プロパティで "Number Pad" (UIKeyboardTypeNumberPad) を指定することで、編集時に数字のみを入力可能なキーボードを表示させることができるようになっています。
ただ、このナンバーパッド・キーボードでは、Enter キーが配置されないため、入力を終了させるためには何らかのボタンなどを用意する必要がでてきます。
UIView のどこかにボタンを配置して、それをタップしてもらう方法もありますけど、ナンバーパッド・キーボードの左下隅にひとつ空きがありますので、そこに Enter キーを配置してみようと思います。
なお、ここでお話しする方法は、iOS SDK 5.0 で動作確認を行っています。
iOS SDK 3.2 以上では、UITextField の inputView プロパティや inputAccessoryView プロパティを使ったカスタムキーボードで実装を行い、標準キーボードにボタンを重ねる方法は使えないといった情報もありましたけど、いろいろ工夫してみたところ、iOS 5.0 でもボタンを重ねることができました。
カスタムキーボードによる方法だと、標準キーボードの上にボタンを追加するか、標準キーボードと差し替えて、必要なキーの処理を全て自前で記載しなければいけないようだったので、必要なボタンだけを重ねられる、この方法を取ってみることにします。
キーボードが表示されるタイミングを検出する
キーボードが表示されたかどうかを検知するには、NSNotificationCenter による通知機能を使用します。
たとえば、目的の UITextField を配置した UIView の "viewWillAppear:" メソッド等で、次のようにして、キーボードの表示を検知できるようにしておきます。
// キーボードが表示される直前に "keyboardWillShow:" メソッドが呼び出されるようにします。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
このようにすることで、キーボードが表示される直前 (UIKeyboardWillShowNotification) に、自分自身 (self) に実装された "keyboardWillShow:" メソッドが呼び出されるようになりました。
また、キーボード表示を検出する必要がなくなったら、登録を解除する必要があるので、それについて先に触れておくことにします。
// キーボード表示を検出する必要がなくなった段階で実行します。
[[NSNotificationCenter defaultCenter] removeObserver:self];
このような処理を、たとえば "viewDidDisappear:" メソッドなどで呼び出しておきます。
キーボードに Enter キーを重ね合わせて表示する
キーボードが表示された際に NSNotificationCenter が呼び出してくれるように登録した "keyboardWillShow:" というメソッドでは、表示されたキーボードの上に、自前で用意した Enter キーを重ねて表示するという処理を記載します。
このとき、表示されるキーボードが Number Pad かどうかにかかわらず呼びだされるので、実際にはいろいろ配慮が必要ですけど、今回は必ず縦画面でナンバーパッドが表示されることを前提にして、話を進めて行きます。
ナンバーパッド・キーボードに Enter キーを重ね合わせる処理は、次のような感じになります。
- (void)keyboardWillShow:(NSNotification*)note
{
// 重ね合わせ用のボタンを用意します。
UIButton* enterButton = [UIButton buttonWithType:UIButtonTypeCustom];
// とりあえず、ボタン上には "Enter" と表示させ、背景色は水色にしておくことにします。
[enterButton setTitle:@"Enter" forState:UIControlStateNormal];
[enterButton setBackgroundColor:[UIColor colorWithRed:0.5765f green:0.6627f blue:0.9020f alpha:1.0000f]];
// ボタンが押されたとき (Touch Up Inside) に enterButton: メソッドが呼び出されるようにします。
[enterButton addTarget:self action:@selector(enterButton:) forControlEvents:UIControlEventTouchUpInside];
// ボタンを表示させるウィンドウはアプリケーションの 2 番目 (Index=1) のウィンドウで良さそうでした。
UIWindow* window = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
// キーボードの位置と、表示時間を NSNotification の userInfo から取得します。
CGRect keyboardFrame = [[note.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat duration = [[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// ボタンを表示させる場所を用意します。キーボードのスライド表示に似せるため、移動前 (startFrame) と移動後 (fixedFrame) の 2 つの位置を用意しました。
CGRect startFrame = CGRectMake(-3.0f, 427.0f +
CGRectGetHeight(keyboardFrame), 108.0f, 53.0f);
CGRect fixedFrame
= CGRectMake(-3.0f, 427.0f, 108.0f, 53.0f)
// ボタンをアニメーションで表示させるために、まずは最初の場所(画面外)に表示されるように frame を設定します。
enterButton.frame = startFrame;
// ボタンは 1 番目 (Index=0) のサブビューとして追加しないと、角が丸く切り抜かれない場合があるようでした。
[window insertSubview:enterButton atIndex:0];
// 適切な位置へボタンが移動するように、アニメーションを設定します。
[UIView beginAnimations:@"showKeyboardButton" context:NULL];
[UIView setAnimationDuration:duration];
enterButton.frame = fixedFrame;
[UIView commitAnimations];
}
アニメーションはそこまでこだわる必要がなければ不要でしょうけど、NSNotificationCenter から userInfo として丁寧に "UIKeyboardAnimationDurationUserInfoKey" という名前で取得できるようになっていました。
それを使って、同様に userInfo から "UIKeyboardFrameEndUserInfoKey" の値を取得して、ーボード 1 つ分移動するようにしてあげると、ちょうどいい具合に見えるようになりました。
このような感じで、UITextField がタップされて、キーボードが表示される際に、標準のキーボードと併せて、自前で用意した Enter キーが表示されるようになりました。
Enter キーを押した際には、プログラム内で登録した "enterButton:" メソッドが呼び出されるので、その中で、Enter キーが押されたときに必要な処理を実装してあげる感じになります。
重ね合わせて表示した Enter キーを非表示にする
続いて、重ねあわせて用意した Enter キーを非表示にする方法について考えてみます。
これまでお話ししてきた実装方法では、キーボードを非表示にする際には、標準キーボードと併せて、自前のボタンも消去してあげる必要があります。
表示されている自前の Enter キーをどこかに確保しておいて、NSNotificationCenter で "UIKeyboardWillHideNotification" 通知を受け取った際に非表示にするという手もあると思いますけど、今回は Enter キーというのもあって、Enter キーが押された時点で呼び出されるメソッド内で、キーボードを非表示にする方法を採ってみます。
この方法なら Enter キー (UIButton) が押されたときには、押されたボタンが第一引数に渡されるので、自作の Enter キーをどこかに保管しておかなくても大丈夫です。
ただし、より細やかな制御を行うには、自前の Enter キーをクラス変数に保持するなどして、いつでも表示・非表示を切り替えたり、移動できたりできるようにしておいた方が良いかもしれません。
以下のキーボード非表示の場合も、ボタンが押されたときのメソッドで処理する方法では、キーボードの高さやアニメーションの時間といった情報は渡されないため、それを表示時にどこかに保存しておくか、即座に消してしまうかなどが必要になりました。
もっとも、非表示については、ボタンがいちばん下というのもあって、表示の時ほどシビアな感じではないので、今回はキーボードの高さを 216 で、アニメーションの時間を 0.25 秒とみなして、アニメーションを実装しておこうと思います。
- (void)enterButton:(UIButton*)sender
{
// 標準キーボードを非表示にします。
[self.view endEditing:YES];
// 自前の Enter キーをアニメーションで、キーボードと一緒に画面外へ移動します。
[UIView beginAnimations:@"hideKeyboardButton" context:NULL];
[UIView setAnimationDuration:0.25f];
sender.frame = CGRectMake(-3.0f, 427.0f + 216.0f, 108.0f, 53.0f);
[UIView commitAnimations];
// キーボードが消え終わった頃合い(ここでは 0.3 秒後)で、自前の Enter キーを解放します。
[sender performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:0.3f];
}
このような感じで、まるでナンバーパッド・キーボードに Enter キーが用意されているみたいに、一体になってキーが移動するようになりました。
別のキーボードタイプに切り替わった場合
別の UITextField をタップした時など、キーボードがナンバーパッドから別のキーボードに変更になる場合があります。
このとき、どうやら NSNotificationCenter からは、キーボード制御を通知するための "UIKeyboardWillShowNotification" や "UIKeyboardWillChangeFrameNotification" といった通知は届いてこない様子でした。
つまり、ある UITextField ではナンバーパッドが設定されていて、別の UITextField では通常のキーボードが設定されていたりする場合、キーボードが変わっても、そのままナンバーパッド用の Enter キーが表示されてしまうという問題が発生してしまいます。
これを回避するための良策は見つかっていないのですけど、考えられるところとしては、UITextField の "Editing Did Begin" イベントや "Editing Did End" イベントを使用して、UITextField を移動したことを検知して、移動先の UITextField に応じて、自前の Enter キーをどうするかを決めるという感じになるのでしょうか。
このようなときには、自前の Enter キーをクラス変数などに確保しておかないと、自前の Enter キーを取得するのもひと苦労なので、viewDidLoad 等のあらかじめ手頃なタイミングで、Enter キーのインスタンスをクラス変数に保存しておくのが良さそうです。
ボタンを重ねて表示するというような無理やりなことをやると、さまざまな場面を想定しようとしたときに、こういった細かな制御でいろいろ苦労が伴いますね。
ちなみに、UITextField のキーボードの種類を調べるには、UITextField が実装している UITextInputTraits プロトコルのプロパティ "keyboardType" を参照することで、確認することができるようになっています。
[ もどる ]