UITextView がキーボードに隠れないようにする : Objective-C プログラミング
PROGRAM
UITextView がキーボードに隠れないようにする
大き目の UITextView を View に配置していると、それを編集しようとしたときに、キーボードの後ろに下のほうが隠れてしまう場合があります。
そういうときには、キーボードが表示されたときに UITextView の frame の y 座標や高さを小さくして、キーボードよりも上までに収まるようにすることで、キーボードに隠されてしまうことを避けるようにします。
例えば aTextView という名前の UITextView を配置している UIViewController があったとします。
元の状態を記憶する場所を用意
まず、UIViewController から派生したクラスで、UITextView の元の大きさを保存しておくための変数 _textViewFrameSaved を用意します。
@interface EzViewController : UIViewController
{
// ここに UITextView の元の大きさを保存します。
CGRect _textViewFrameSaved
}
ここに、キーボードが表示されたときに UITextView の元の状態を保存して、大きさをキーボードに重ならないように調整して、キーボードが非表示にされたときに、ここに保存しておいたサイズに戻すようにします。
キーボードが非表示になったときにキーボードの大きさから元の位置を割り出す方法もあるかもしれませんが、今回はこのようにしてみます。
ここに値が設定されていれば、キーボードが表示されていて UITextView の大きさが変更されているということも判るようになるので、そのほかの制御もしやすいように思います。
キーボードの表示や非表示通知をハンドル
続いて、キーボードが表示されたかどうかを検出するために、viewWillAppear メソッドが呼ばれたタイミングで、UIKeyboardDidShowNotification と UIKeyboardDidHideNotification を受け取るように設定します。
// viewWillAppear はビューが表示される直前に呼び出されます。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// キーボード表示関連の通知を受け取るようにします。
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
}
あわせて、viewDidDisappear メソッドが呼ばれたタイミングで、これらの通知を受け取らないように設定します。
キーボードの状態に応じて UITextView をリサイズ
これらの準備が整ったら、上記でキーボードが表示されたタイミングで呼び出されるようにした "keyboardDidShow" メソッドを実装して、その中で、キーボードと重ならないように UITextView の大きさを小さくするコードを記載します。
ここで、キーボードの大きさを取得するのに、ビューの回転状態も考慮に入れた キーボードの位置や大きさを取得する で紹介したメソッドを使用します。
// UIKeyboardDidShowNotification 通知が来たら実行するように登録したメソッドです。
- (void)keyboardDidShow:(NSNotification*)note
{
// 状態が保存されている場合は既にキーボードが表示されているものとして、それ以外のときに処理を行います。
if (CGRectIsEmpty(_textViewFrameSaved))
{
// キーボードが表示された後のキーボードの大きさを、キーボードの位置や大きさを取得する で紹介したメソッドで取得しています。
CGRect keyboardRect = [self keyboardRectByNotification:note userInfoKey:UIKeyboardFrameEndUserInfoKey orientation:self.interfaceOrientation];
// 元のフレームサイズを保存しておきます。
_textViewFrameSaved = aTextView.frame;
// UITextView がキーボードに隠される部分を調べます。
CGRect frameChanged = [aTextView convertRect:aTextView.frame toView:self.view];
CGFloat acceptableHeight = self.view.frame.size.height - keyboardRect.size.height;
CGFloat bottomOfFrameChanged = frameChanged.origin.y + frameChanged.size.height;
// UITextView がキーボードに隠れる場合は、縮小しなければいけない高さを取得します。
if (acceptableHeight < bottomOfFrameChanged)
{
// 縮小するサイズを取得します。
CGFloat heightReduce = bottomOfFrameChanged - acceptableHeight;
// 縮小後のフレームサイズを計算します。
CGRect frame;
frame = aTextView.frame;
frame.size.height -= heightReduce;
// UITextView をアニメーションでサイズ変更するために、キーボードの表示アニメーション時間を取得します。
NSTimeInterval duration = [[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// アニメーションを開始します。
[UIView beginAnimations:@"reduceTextView" context:nil];
[UIView setAnimationDuration:duration];
// UITextView の高さを縮小します。
aTextView.frame = frame;
// アニメーションを確定します。
[UIView commitAnimations];
}
}
}
後は、キーボードが隠されたときに、UITextView を予め保存しておいたサイズに戻します。
// UIKeyboardDidHideNotification 通知が来たら実行するように登録したメソッドです。
- (void)keyboardDidHide:(NSNotification*)note
{
// キーボードが表示中(元のサイズが保存されている)場合に復元処理を行います。
if (!CGRectIsEmpty(_textViewFrameSaved))
{
// UITextView をアニメーションでサイズ変更するために、キーボードの表示アニメーション時間を取得します。
NSTimeInterval duration = [[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// アニメーションを開始します。
[UIView beginAnimations:@"restoreTextViewFrame" context:nil];
[UIView setAnimationDuration:duration];
// UITextView のサイズを復元します。
aTextView.frame = _textViewFrameSaved;
// アニメーションを確定します。
[UIView commitAnimations];
// 保存しておいた UITextView の元のサイズをリセットします。
_textViewFrameSaved = CGRectZero;
}
}
これで、キーボードの表示にあわせて UITextView が隠れてしまう部分を詰めて、編集操作が出来るようになりました。
ちなみにここでは UIKeyboardDidHideNotification を利用して UITextView のサイズ復元をしていますけど、これを UIKeyboardWillHideNotification を利用した場合、ユーザーインターフェイスを回転させた場合に不都合が出る場合があるようでした。
これはどうやら デバイス回転時の通知やメソッドの実行の流れ でも記しましたけど、WillHide の方はユーザーインターフェイスの回転が完了するよりも前に呼び出されるため、レイアウトの調整が思うようにいかない場合があるようでした。DidHide の方は回転が完了した後に呼ばれます。
WillShow で処理が出来た方が、キーボードのアニメーションとぴったり合わせて UITextView のサイズ復元処理ができてきれいなのですけど、上記のような理由から、ここではキーボードが表示された後に復元する形にしてあります。
[ もどる ]