キーボードに合わせて画面を上げ下げする
画面の下の方に TextField がある場合、キーボードが競り上がっても隠れないよう、キーボードが競り上がるのに合わせて画面をにょいっと上げてやる処理が必要になります。
今回は、UIEdgeInsets に関連する Tips 第2段として、キーボードに合わせて画面を上げ下げする方法を紹介します。
まずは、”Lorem ipsum…” UITextView、”Name:” UILabel、UITextField を、UIScrollView に入れます。このビュー構成で次のことをします:
- キーボードが上がり下がりするタイミングを知らせてもらえるようにする
- キーボードが競り上がるのに合わせて、画面を上げる
- キーボードの Done ボタンが押されたら、キーボードを隠す
- キーボードが隠れるのに合わせて、画面を下げる
キーボードが上がり下がりするタイミングを知らせてもらえるようにする
キーボードが上がり下がりするタイミングは、UIKeyboardWillShowNotification と UIKeyboardWillHideNotification をオブザーブすることで知ることができます。ビューコントローラの viwWillAppear: でオブザーブ開始、viewWillDisappear: でオブザーブ終了するとよいでしょう。
- (void)viewWillAppear:(BOOL)animated
{
// super
[super viewWillAppear:animated];
// Start observing
if (!_observing) {
NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[center addObserver:self
selector:@selector(keybaordWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
_observing = YES;
}
}
- (void)viewWillDisappear:(BOOL)animated
{
// super
[super viewWillDisappear:animated];
// Stop observing
if (_observing) {
NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[center removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
_observing = NO;
}
}
これで、キーボードが競り上がり始める時に keyboardWillShow:、キーボードが隠れ始める時に keyboardWillHide: が呼ばれるようになりました。
キーボードが競り上がるのに合わせて、画面を上げる
keyboardWillShow: メソッドで、次のアニメーションを行います:
- キーボードに被る分、scrollView の中身に余白をつける
- textField が常にキーボードのすぐ上に表示されるように、scrollView を一番下にスクロールさせる
「キーボードに被る分、scrollView の中身に余白をつける」の考え方は、前回の「UIScrollView を半透明なバーに適合させる」と同じ。今回は、それをアニメーション付きで行っているだけです。
- (void)keyboardWillShow:(NSNotification*)notification
{
// Get userInfo
NSDictionary *userInfo;
userInfo = [notification userInfo];
// Calc overlap of keyboardFrame and textViewFrame
CGFloat overlap;
CGRect keyboardFrame;
CGRect textViewFrame;
keyboardFrame = [[userInfo
objectForKey:UIKeyboardFrameEndUserInfoKey]
CGRectValue];
keyboardFrame = [_scrollView.superview
convertRect:keyboardFrame
fromView:nil];
textViewFrame = _scrollView.frame;
overlap = MAX(0.0f,
CGRectGetMaxY(textViewFrame) - CGRectGetMinY(keyboardFrame));
// Calc insets of _scrollView
UIEdgeInsets insets;
insets = UIEdgeInsetsMake(0.0f, 0.0f, overlap, 0.0f);
// Animate insets of _scrollView
NSTimeInterval duration;
UIViewAnimationCurve animationCurve;
void (^animations)(void);
duration = [[userInfo
objectForKey:UIKeyboardAnimationDurationUserInfoKey]
doubleValue];
animationCurve = [[userInfo
objectForKey:UIKeyboardAnimationCurveUserInfoKey]
integerValue];
animations = ^(void) {
// Set insets of _scrollView
_scrollView.contentInset = insets;
_scrollView.scrollIndicatorInsets = insets;
};
[UIView
animateWithDuration:duration
delay:0.0
options:(animationCurve << 16)
animations:animations
completion:nil];
// Scroll to bottom
CGRect rect;
rect.origin.x = 0.0f;
rect.origin.y = _scrollView.contentSize.height - 1.0f;
rect.size.width = CGRectGetWidth(_scrollView.frame);
rect.size.height = 1.0f;
[_scrollView scrollRectToVisible:rect animated:YES];
}
キーボードの Done ボタンが押されたら、キーボードを隠す
Done ボタンが押されたことを知るには、textField の delegate になり -textFieldShouldReturn: メソッドを実装します。ここで、textField を resignFirstResponder させれば、キーボードは隠れることになります。
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
// Hide keyboard
[textField resignFirstResponder];
return YES;
}
キーボードが隠れるのに合わせて、画面を下げる
keyboardWillHide: メソッドで、scrollView の contentInset と scrollIndicatorInsets を元に戻します。
- (void)keybaordWillHide:(NSNotification*)notification
{
// Get userInfo
NSDictionary *userInfo;
userInfo = [notification userInfo];
// Animate insets of _scrollView
NSTimeInterval duration;
UIViewAnimationCurve animationCurve;
void (^animations)(void);
duration = [[userInfo
objectForKey:UIKeyboardAnimationDurationUserInfoKey]
doubleValue];
animationCurve = [[userInfo
objectForKey:UIKeyboardAnimationCurveUserInfoKey]
integerValue];
animations = ^(void) {
// Set insets of _scrollView
_scrollView.contentInset = UIEdgeInsetsZero;
_scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
};
[UIView
animateWithDuration:duration
delay:0.0
options:(animationCurve << 16)
animations:animations
completion:nil];
}

