緣由
看了很多輸入框,處理的最好的,就是微信,QQ,以及支付寶,其他的看了不少,包括百思,簡書,same.微信,QQ,支付寶的就不做gif了。百思之前的版本輸入框做的也不好,后來反饋了之后換了一種方式而已。拿簡書和same舉個列子。(ps:same 是同事推薦,我也不知道是什么軟件,此處沒有打廣告)
簡書:

可以看到在返回的時候輸入框會跳躍到鍵盤下方,然后很快再出來。(做的好的會一體返回)

問題2:在限制多行之后,就會出現(xiàn)最上面的一行顯示不全。
same

問題:same的輸入框和簡書的如出一轍,在側(cè)滑返回的時候會跳躍。
有兩種思路,推薦第二種。第一種因?yàn)橛邢到y(tǒng)默認(rèn)的上下間距,動畫效果不好。第二種我們可以關(guān)閉掉系統(tǒng)的默認(rèn)間距,為textView添加一個假的上下間距,動畫會比較自然。
第一種:利用textview自定義鍵盤,限制多行輸入。
關(guān)于跳躍問題,實(shí)現(xiàn)一體鍵盤,參考文章:iOS 怎么做成和微信一樣的一體鍵盤?
限制多行問題,討論一下。
UITextView有默認(rèn)的上下間距是8,打印textContainerInset是可以測出來的。微信的輸入框在單行的時候保持了默認(rèn)的上下間距8,但是在大于一行文字后上下間距就會變化。
為了做的和微信輸入框很像,我們在限制了最大行數(shù)之后,可以設(shè)置textView的setContentOffset屬性,滾動距離為(當(dāng)前行數(shù) - 限制的最大行數(shù))* lineHeight + 上邊距。即可。為了過渡效果,在超出限制最大行數(shù)時候關(guān)閉掉系統(tǒng)的默認(rèn)動畫[self.textView setContentOffset:point animated:NO],否則在回退的時候會很丑,在小于最大行數(shù)是使用系統(tǒng)的動畫效果[self.textView setContentOffset:CGPointZero animated:YES];
if (line >self.maxLine) {
CGPoint point = CGPointMake(0, (line -self.maxLine) *lineH +8 );
[self.textView setContentOffset:point animated:NO];
self.textView.lx_height = ceil(self.maxLine *lineH +8);
}else{
self.textView.lx_height = contentSizeH;
[self.textView setContentOffset:CGPointZero animated:YES];
還有一點(diǎn)需要說明,輸入框的初始高度,因?yàn)閠extView的默認(rèn)上下間距,我們需要計(jì)算好初始高度,經(jīng)過測試,如果textView font大小為16時,系統(tǒng)的默認(rèn)高度最好設(shè)置為36. 當(dāng)textView font大小為16時,lineHeight為19.09,加上上下間距為35.09,系統(tǒng)取了36.
所以初始化高度為:
CGFloat orignTextH = ceil (TEXTFONT.lineHeight + 16);
self.frame = CGRectMake(0, Device_Height - orignTextH, Device_Width, orignTextH);
一體鍵盤的處理:
-(void)keyboardWillChangeFrame:(NSNotification *)notification{
NSDictionary *userInfo = notification.userInfo;
// 動畫的持續(xù)時間
double duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 鍵盤的frame
CGRect keyboardF = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// NSLog(@"%@",NSStringFromCGRect(keyboardF));
keyboardY = keyboardF.origin.y;
if (!_isDisappear) {
[self dealKeyBoardWithKeyboardF:keyboardY duration:duration];
}
}
-(void)keyboardDidChangeFrame:(NSNotification *)notification{
NSDictionary *userInfo = notification.userInfo;
// 動畫的持續(xù)時間
double duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 鍵盤的frame
CGRect keyboardF = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// NSLog(@"%@",NSStringFromCGRect(keyboardF));
keyboardY = keyboardF.origin.y;
// // 工具條的Y值 == 鍵盤的Y值 - 工具條的高度
if (_isDisappear) {
[self dealKeyBoardWithKeyboardF:keyboardY duration:duration];
}
}
#pragma mark---處理高度---
-(void)dealKeyBoardWithKeyboardF:(CGFloat)keyboardY duration:(CGFloat)duration {
if (!_isDisappear) {
[UIView animateWithDuration:duration animations:^{
// 工具條的Y值 == 鍵盤的Y值 - 工具條的高度
if (keyboardY > Device_Height) {
self.lx_y = Device_Height- self.lx_height;
}else
{
self.lx_y = keyboardY - self.lx_height;
}
}];
}else{
if (keyboardY > Device_Height) {
self.lx_y = Device_Height- self.lx_height;
}else
{
self.lx_y = keyboardY - self.lx_height;
}
}
}
第二種 關(guān)閉掉系統(tǒng)對
textView的默認(rèn)間距。
如圖,我們給textView一個上下間距,然后再做動畫就比較自然.

#pragma mark---getter---
-(LXTextView *)textView{
if (!_textView) {
_textView =[[LXTextView alloc]initWithFrame:CGRectMake(0, self.topOrBottomEdge, self.lx_width - 60, ceil(self.font.lineHeight))];
_textView.font = self.font;
_textView.delegate = self;
_textView.layoutManager.allowsNonContiguousLayout = NO;
_textView.enablesReturnKeyAutomatically = YES;
_textView.scrollsToTop = NO;
_textView.textContainerInset = UIEdgeInsetsZero; //關(guān)閉textview的默認(rèn)間距屬性
_textView.textContainer.lineFragmentPadding = 0;
_textView.placeholder = @"發(fā)表評論:";
}
return _textView;
}
在處理文字變化的代理方法更改如下:
-(void)textViewDidChange:(UITextView *)textView{
CGFloat contentSizeH = self.textView.contentSize.height;
CGFloat lineH = self.textView.font.lineHeight;
CGFloat maxHeight = ceil(lineH * self.maxLine + textView.textContainerInset.top + textView.textContainerInset.bottom);
if (contentSizeH <= maxHeight) {
self.textView.lx_height = contentSizeH;
}else{
self.textView.lx_height = maxHeight;
}
[textView scrollRangeToVisible:NSMakeRange(textView.selectedRange.location, 1)];
CGFloat totalH = ceil(self.textView.lx_height) + 2 * self.topOrBottomEdge;
self.frame = CGRectMake(0, keyboardY - totalH, self.lx_width, totalH);
self.sendBtn.lx_height = totalH;
self.line.lx_height = totalH - 10;
}
補(bǔ)充一點(diǎn):為了適配iOS11以下,需要處理textView,否則xcode9寫的代碼在iOS11以下的設(shè)備上跑的時候回跳動。
textView做如下處理:
if (@available(iOS 11.0, *)) {
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
控制器中添加處理:
if (@available(iOS 11.0, *)) {
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// 這部分使用到的過期api
self.automaticallyAdjustsScrollViewInsets = NO;
#pragma clang diagnostic pop
}

demo中沒有第一種效果,上面就是探路的思路。demo中的第二種效果支持代碼以及XIB創(chuàng)建keyboard.
demo 地址:一體鍵盤,限制最大行數(shù)
博客推薦:https://4xx.me