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 のサイズ復元処理ができてきれいなのですけど、上記のような理由から、ここではキーボードが表示された後に復元する形にしてあります。

[ もどる ]