A.) Apple Notes のスクロール + キーボードの非表示と同様の効果を実現するために、UIScrollView 内に UITextView を追加することから始めました。
B.) 次に、キーボードの変更のオブザーバーとして self を追加します。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
C.) 次に、ユーザーが textview / textfield にアクセスすると、以下のメソッドが呼び出されます。キーボードの開始高さと終了高さを取得し、keyboardHeight という変数を endHeight 値 (後で使用) に設定します。
以下で計算される「差異」は、ユーザーが自動修正/提案バーを上下に移動するときに、テキストを上下に移動するために使用されます。これを望まない場合は、textView の「autocorrectionType」を false に設定できます。
-(void)keyboardWillChangeFrame:(NSNotification *)n {
float beginHeight = [[n.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
float endHeight = [[n.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
float difference = beginHeight-endHeight;
keyBoardHeight = endHeight; //set the var
//animate change
if (difference != 0){ //animate frame change in your textview }
}
D.) textViewDidBegin メソッドで、UIScrollview を画面の一番上に移動して、textview が一番上と同じ高さになるようにします (setContentOffset)。
テキストビューの高さは、ビューの上部とキーボードの上部の間にある必要があるため、以下で「initialTVHeight」と呼ばれます。
textview のスクロールを強制的に有効にする必要があるため、textView.contentSize を最小サイズ (initialTVHeight) + 1 ピクセルに設定します。これによりスクロールが強制されます。
-(void)textViewDidBeginEditing:(UITextView *)textView {
//need to move the tv up to the top
float yOff = 0;
yOff += durationCell.frame.size.height;
yOff += reminderCell.frame.size.height;
yOff += fixedCell.frame.size.height;
yOff += repeatCell.frame.size.height;
[mainScroller setContentOffset:CGPointMake(0, yOff) animated:true];
//resize the textview to meet the top of the keyboard
float padding = 0; //padding between keyboard and last line in the textview
initialTVHeight = h - 60 - keyBoardHeight - padding; //the height when text does not overflow
textTV.frame = CGRectMake(0, textTV.frame.origin.y, w, initialTVHeight); //set the frame to that size
//if smaller than minimum, increase (if editing existing text)
if (textTV.contentSize.height < initialTVHeight){
textTV.contentSize = CGSizeMake(w, initialTVHeight+1); //force initial scroll
}
}
E.) このメソッドは、textView の contentSize を最小の高さに更新して、スクロールが有効になるようにします (つまり、テキストが 2 行しかない場合でも、ユーザーはスクロールをバウンスできます)。textView コンテンツが初期サイズよりも長い場合は、自然に大きくなるようにします。
-(void)textViewDidChange:(UITextView *)textView {
//require minimum height for scroll
if (textTV.contentSize.height < initialTVHeight){ //content height comes in under, force scroll
textTV.contentSize = CGSizeMake(w, initialTVHeight+1); //adding one forces scroll
}
}
F.) これらのメソッドを追加して、(ユーザーが新しい行を入力するのではなく) ユーザーがドラッグすることによってのみキーボードが閉じられるようにします。
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if ([scrollView isEqual:textTV]){
enableDragDismiss = true;
}
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
if ([scrollView isEqual:textTV]){
enableDragDismiss = false;
}
}
G.) 最後に、このメソッドを追加します。このメソッドは、ユーザーがキーボードの高さより下にドラッグした場合にキーボードを閉じます。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (enableDragDismiss && [scrollView isEqual:textTV]){
float y = [textTV.panGestureRecognizer locationInView:self.view].y;
float keyboardY = h-keyBoardHeight;
if (y > keyboardY){
enableDragDismiss = false; //allow only once
[textTV resignFirstResponder];
[UIView animateWithDuration:0.5f
delay:0.0f
usingSpringWithDamping:1.0f
initialSpringVelocity:0.8f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
mainScroller.contentOffset = CGPointMake(0, 0);
textTV.contentOffset = CGPointMake(0, 0);
}
completion:^(BOOL finished){
}];
float yOff = 60;
yOff += durationCell.frame.size.height;
yOff += reminderCell.frame.size.height;
yOff += fixedCell.frame.size.height;
yOff += repeatCell.frame.size.height;
textTV.frame = CGRectMake(0, textTV.frame.origin.y, w, h-yOff);
}
}
}