iOS项目开发键盘弹出遮挡输入框问题解决方案
在ios或android等移动端开发过程中,经常遇到很多需要我们输入信息的情况,例如登录时要输入账号密码、查询时要输入查询信息、注册或申请时需要填写一些信息等都是通过我们键盘来进行输入的,在ios开发过程中,一般用于进行输入信息的有两类:uitextfield和uitextview,前者是单行输入文本框,后者是可滑动的多行输入文本框,在这整个开发过程中,我们需要控制键盘的弹出和收起、在输入结束的时候获取输入的信息,此外,我们还需要保证在键盘弹起的时候不遮挡我们输入的文本框。今天,我们就主要来说一下文本框输入的完整响应过程以及最后这个遮挡问题的解决方案。
一 文本框输入完整的响应过程
首先,我们要对uitextfield和uitextview的输入输出进行控制,需要借助其对应的代理协议uitextfielddelegate或uitextviewdelegate中的一些方法,两者在控制文本框中的相应流程稍有差别,但是都差不多,接下来,我们就从官方文档中开学习一下文本框输入完整的响应过程。在uitextfielddelegate的官方文档中解释道:我们可以通过代理中的一些方法实现uitextfield对键盘的调用,从而实现和用户进行交互的方法,此外,还可控制uitextfield的输入过程。uitextfield的整个输入过程分为如下7个步骤(如下过程,textfield换成textview即是textview的响应过程):
在成为第一响应者之前,文本框调用其代理的 textfieldshouldbeginediting: 方法来允许或阻止其第一响应者,并控制是否对文本框进行输入
成为第一响应者,对应的相应事件就是系统调用键盘(自动弹出),并且系统会根据需要发出uikeyboardwillshownotification 和uikeyboarddidshownotification的notification通知,而如果此时系统中有其他的输入视图是可视的,则系统会发出 uikeyboardwillchangeframenotification和uikeyboarddidchangeframenotification的通知
系统调用代理的 textfielddidbeginediting: 方法,并且发出uitextfieldtextdidbegineditingnotification的通知,此时光标已经在text field中定位了,键盘也已经弹出来了,接下来可以进行输入了
在输入信息过程中,当前文本内容改变就会调用 textfield:shouldchangecharactersinrange:replacementstring: 方法,并且会发出uitextfieldtextdidchangenotification的通知。此外,当用户点击【clear/清除】按键时调用 textfieldshouldclear: 方法清除内容,当用户点击【return/完成】按键时调用 textfieldshouldreturn: 方法,注意:uitextviewdelegate没有对应清除和完成方法,所以我们不能调用 textfieldshouldclear: 方法和 textfieldshouldreturn: 方法实现【clear/清除】和【return/完成】按键的效果
在文本框输入即将结束,即即将注销第一响应者时,系统会调用 textfieldshouldendediting: 方法
文本框注销第一响应者,对应的响应时间就是系统收回键盘,并且在隐藏键盘时会发出 uikeyboardwillhidenotification和uikeyboarddidhidenotification的通知
最后,系统调用 textfielddidendediting: 方法结束输入,并发出uitextfieldtextdidendeditingnotification的通知。
二 键盘的弹出与收起
2.1 弹出键盘的控制
关于键盘的弹出与收起问题,从上面的响应过程分析可知,键盘的弹出对于uitextfield和uitextview都是自动弹出的,所以我们无需进行控制。如果需要进行控制,我们知道其实在弹出之前是调用了uitextfielddelegate或uitextviewdelegate的以下方法来控制是否使当前文本框设置为第一响应者( becoming the first responder),文本框成为第一响应者的结果就是可以输入文本内容并弹出键盘,所以,我们可以在这个方法中通过返回值来判断是否弹出键盘。
//uitextfield调用此方法 - (bool)textfieldshouldbeginediting:(uitextfield *)textfield{ //返回yes是弹出键盘 返回no则不弹出键盘 } //uitextview调用此方法 - (bool)textviewshouldbeginediting:(uitextview *)textview{ //返回yes是弹出键盘 返回no则不弹出键盘 }
2.1 收起键盘的控制
通过前面的分析我们知道,收起键盘主要是要注销文本框的第一响应者身份即可,所以,我们再需要控制键盘收起的时候调用 textfieldshouldendediting: 方法即可达到目的。关于收起键盘,我们通常有两种做法如下:
一是通过我们的键盘上的【return/完成】按键的点击事件来设置
二是设置点击空白处就收起键盘,这种方法现在比较普遍
这两种做法在uitextfield和uitextview上的实现在我之前的随笔中有专门讲到过了,想了解的小伙伴可以直接戳这里:
三 文本框内容的获取
在之前的分析中,我们知道,文本框在结束之后都会调用一个方法就是 textfielddidendediting: / textviewdidendediting: 方法,这个方法就是方便我们在输入结束之后对文本框内容进行处理。如果我们开发过程中一个页面有多个相同类型的输入文本框,我们可以通过设置不同的tag来区分当前的是哪一个输入文本框,从而进行不同的处理,具体示例如下:
- (void)textviewdidendediting:(yytextview *)textview{ if (textview.tag == 400) { nsstring *reason = textview.text; [self.submitinfodic setobject:reason forkey:@"reason"]; } else { nsstring *remark = textview.text; [self.submitinfodic setobject:remark forkey:@"remark"]; } }
四 键盘弹出的遮挡问题
还是在之前的分析中,我们知道在键盘弹出和收起时,系统都会发出对应的通知,所以我们可以在收到键盘弹出的时候判断键盘的位置和当前输入文本框的位置,如果有遮挡,就将当前视图进行一个向上平移,在收到键盘回收的通知时就平移到原先的位置。所以,主要分为2步:
注册键盘弹出和收起的通知事件
#pragma mark notification 通知管理 /** * @brief 通知注册 * @return */ - (void)registnotification { // observe keyboard hide and show notifications to resize the text view appropriately [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(keyboardwillshow:) name:uikeyboardwillshownotification object:nil]; [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(keyboardwillhide:) name:uikeyboardwillhidenotification object:nil]; }
实现收到键盘弹出和收起通知事件的响应事件
#pragma mark --键盘弹出收起管理 -(void)keyboardwillshow:(nsnotification *)note{ cgrect frame = self.textviewframe; //获取键盘高度 nsdictionary* info = [note userinfo]; cgsize kbsize = [[info objectforkey:uikeyboardframeenduserinfokey] cgrectvalue].size; //140是文本框的高度,如果你的文本框高度不一样,则可以进行不同的调整 cgfloat offset = frame.origin.y + 140 - (self.view.frame.size.height - kbsize.height); //将试图的y坐标向上移动offset个单位,以使界面腾出开的地方用于软键盘的显示 if (offset > 0.01) { weakself [uiview animatewithduration:0.1 animations:^{ weakself.tableview.contentoffset = cgpointmake(0, offset); }]; } } -(void)keyboardwillhide:(nsnotification *)note{ [uiview animatewithduration:0.1 animations:^{ self.tableview.contentoffset = cgpointmake(0, 0); }]; }
多时候,我们有多个输入文本框,在我们的示例中,我们就有两个输入文本框,这时候我们收到通知的时候怎么判断是哪个文本框呢?在前的分析中,我们知道,在发出通知之前,系统会调用输入文本框代理的 textfieldshouldbeginediting: 方法来判断是否允许编辑,那么我们可以在这个方法中判断是哪一个文本框以及文本框的具体位置等等,然后在键盘弹出时通过为止比较确定是否平移,以及平移的offset。
- (bool)textviewshouldbeginediting:(yytextview *)textview{ //获取当前输入文本框相对于当前view的位置 self.textviewframe = [textview convertrect:textview.frame toview:self.view]; return yes; }