在开发的过程中,难免遇到在一个页面中用户需要输入许多的数据,也有可能跳转的下一个页面也涉及到多个输入框。
如果产品经理一意孤行的话,那么只能和键盘进行斗争了。因为刚接手项目时第一次发现由于监听导致页面上移等乱七八糟的问题,所以以下图的两个页面中的textField为例:
版本一
刚接手项目的第一个版本,即将上线前一天出现了页面滑动的错误。
类似于上面的例图,两个跳转页面中都需要进行键盘的监听。而出现问题的主要原因是:两个页面中的键盘监听互相影响了。
对键盘的出现和消失监听的位置一般在viewDidLoad方法中
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showKeyboardHandler:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hiddenKeyboardhandler:) name:UIKeyboardWillHideNotification object:nil];
移除对键盘的监听在viewWillDisappear方法中:
[[NSNotificationCenter defaultCenter] removeObserver:self];
对正常单一页面输入框的处理没有问题,但是在多个页面都存在键盘监听的情况下会出现监听到不合时宜的键盘通知,通过页面跳转方法调用的顺序即可知:
secondVC ——>viewDidLoad 1
firstVC ——>viewWillDisappear 2
secondVC ———>viewWillAppear 3
firstVC ———>viewDidDisappear 4
secondVC ——>viewDidAppear 5
也就是说当页面1还在监听键盘时,页面2也开始监听。如果用户在页面1输入的过程中直接进入页面2,系统回调UIKeyboardWillHideNotification通知给页面1时,这时候页面2也会接收到隐藏键盘的通知。
所以在页面2添加监听前必须除去页面1中的键盘监听方法。
版本二
第一个版本成功上线后,该页面出现了两个新的需求:
① 自定义键盘
之前想着是有什么难的:就是在监听键盘出现的方法中,定义一个CustomKeyboardView放在keyWindow上,覆盖系统的keyboard。实际做了才发现:
将CustomKeyboardView添加到keyWindow上
CustomKeyboardView在keyboardWillShow的通知方法中添加到keyWindow上,只会被系统键盘覆盖。
所以必须将CustomKeyboardView添加到最上方的window上:
window = [[[UIApplication sharedApplication] windows]lastObject];
将CustomKeyboardView覆盖系统的键盘
这种方法系统的键盘还是会弹出,动画效果不是很好,而且textField提供了设置自定义键盘更直接的方式---通过设置inputView:
CustomKeyboard *customKeyboard = [[CustomKeyboard alloc] initWithFrame:CGRectZero];
self.textField.inputView = customKeyboard;
事实证明未实际做的有可能全错。
② 备注的输入框随着行数变化其高度
中午暖暖的阳光中,一看需求的时候,直接想着封装一个基于TextField的view,监听其高度的变化。等下午开始写的时候:
使用TextField计算高度
@interface UITextField : UIControl
UITextField直接继承与UIControl,一行直接写到底,压根就不会换行。能换行的输入框是UITextView:
@interface UITextView : UIScrollView
继承于UIScrollView,定义maxRow,当行数超过该定义值直接将scrollEnabled开启。
计算一行的高度
一般会直接使用系统提供的方法计算label整个内容的高度:
[self.textView.text boundingRectWithSize:CGSizeMake(width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} context:nil]
但是对于textView而言,因为会自动换行,所以只需要计算一行的高度即可:字体的高度 + 距离top的高度约束 + 距离bottom的高度约束:
CGFloat height = ceilf([self.textView sizeThatFits:CGSizeMake(self.textView.frame.size.width, MAXFLOAT)].height);
其实在项目中除了新开发的需求之外,还有一个硬性的优化需求:
在键盘显示的时候如何简单的向上移动被遮盖的输入框?
之前想着通过计算每一个cell中textField在tableView中的(x,y)值来上移一定的距离,但是操作起来还是麻烦不已。因为在项目中整个页面中基本上全是输入框,所以参考了第三方库 IQKeyboardManager,用于处理键盘遮罩的问题。
pod 'IQKeyboardManager'
IQKeyboardManager
该篇中将使用方式和参数的设置进行讲解,具体的等到下一篇中详解。
① 全局参数设置
//设置全局参数
IQKeyboardManager *keyboardManager = [IQKeyboardManager sharedManager];
//开启功能 --->如果全局设置了YES,那么每一个类默认都是开启该功能的。
keyboardManager.enable = NO;
//点击背景是否将键盘收起
keyboardManager.shouldResignOnTouchOutside = YES;
//控制键盘上toolBar的字体颜色是否由用户自己控制
keyboardManager.shouldToolbarUsesTextFieldTintColor = YES;
//当有多个输入框的时,如何识别不同的输入框进行跳转
keyboardManager.toolbarManageBehaviour = IQAutoToolbarBySubviews;
//是否显示键盘上的toolBar
// Automatic add IQToolbar functionality. Default is YES. 自动添加IQToolbar的功能。默认为YES
keyboardManager.enableAutoToolbar = YES;
//是否将textField的placeholder显示
/**
If YES, then it add the textField's placeholder text on IQToolbar. Default is YES.
*/
keyboardManager.shouldShowToolbarPlaceholder = YES;
keyboardManager.placeholderFont = [UIFont systemFontOfSize:15];
//输入框距离键盘顶部的距离
keyboardManager.keyboardDistanceFromTextField = 10.0f;
重点说明一下:
textField发出开始编辑的通知后,IQKeyboardManager会监听键盘的出现消失以及textField、textView的开始编辑和结束编辑的通知,所以当enable=YES后,已经为项目中配置好该功能
所以在弄完上面的配置后,不要怀疑直接在需要的类中开启该功能即可使用,因为全是通知进行沟通。
②textField和textView的发出通知的顺序
在该类中对textField和textView分别进行开始编辑通知的监听以及键盘出现和消失的监听.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
结果很有意思:
textField:
textFieldBeginEditing:
keyboardWillShowHandler:
keyboardDidShowHandler:
textView:
keyboardWillShowHandler:
textViewBeginEditing:
keyboardDidShowHandler:
为两者的不同再加一笔。
在实际的开发中,输入框有挺多值得注意的地方,如使用以下的方法获得变化的text值:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
该方法会导致无法获得最后一个字符,可以使用:
[self.textField addTarget:self action:@selector(changeTextHandler) forControlEvents:UIControlEventEditingChanged]