键盘处理例子思路
步骤一:简单搭建界面
步骤二、自定义 XIB ,用来描述性别
+ ( id )sexBox
{
return [[NSBundle mainBundle] loadNibNamed: @"MJSexBox" owner: nil options: nil ][ 0 ];
}
2.1 监听性别按钮点击,修改按钮的状态
#pragma mark 改变了性别选择
- ( IBAction )sexChange {
if (_manBtn.enabled) { // 点击了男的
_manBtn.enabled = NO ;
_womanBtn.enabled = YES ;
} else { // 点击了女的
_manBtn.enabled = YES ;
_womanBtn.enabled = NO ;
}
}
2.2 .在控制器中 添加性别选择控件
MJSexBox *sexBox = [ MJSexBox sexBox ];
sexBox.center = CGPointMake( 150 , 70 );
[ self .view addSubview:sexBox];
步骤三:自定义键盘
3.1. 设置生日键盘
// 1.1. 生日
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeDate; // 只显示日期 5
datePicker.locale = [[NSLocale alloc] initWithLocaleIdentifier: @"zh_CN" ];
[datePicker addTarget: self action: @selector (birthdayChange:) forControlEvents:UIControlEventValueChanged];
_birthdayField.inputView = datePicker; // 设置键盘为日期选择控件
_birthdayField.delegate = self ;
3.2 禁止生日键盘输入文字
#pragma mark - UITextField 代理
#pragma mark 每当用户输入文字的时候就会调用这个方法,返回 NO ,禁止输入;但会 YES ,允许输入
- ( BOOL )textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
return NO ;
}
3.3 监听生日键盘的值改变事件
#pragma mark - 生日改变
- ( void )birthdayChange:(UIDatePicker *)picker
{
// 1. 取得当前时间
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-dd" ;
NSString *time = [fmt stringFromDate:picker.date];
// 2. 赋值到文本框
_birthdayField.text = time;
}
步骤四、 设置城市
4.1 用 xib 描述城市键盘,并且设置 UIPickerView 的代理和数据源,创建自定义视图。
+ ( id )cityPicker
{
return [[NSBundle mainBundle] loadNibNamed: @"MJCityPicker" owner: nil options: nil ][ 0 ];
}
4.2 加载数据,在 awakeFromNib 中调用
#pragma mark 任何对象从 xib 中创建完毕的时候都会调用一次
- ( void )awakeFromNib
{
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"cities.plist" ofType: nil ]];
_provinces = [NSMutableArray array];
for (NSDictionary *dict in array) {
MJProvince *p = [MJProvince provinceWithDict:dict];
[_provinces addObject:p];
}
}
4.3 实现数据源和代理方法。
#pragma mark - UIPickerView 数据源方法
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2 ;
}
#pragma mark 第 component 列有多少行数据
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if (component == 0 ) { // 多少个省份
return _provinces.count;
} else { // 当前选中省份的行数(城市个数)
// 1. 获得选中了哪一个省
int pIndex = [pickerView selectedRowInComponent: 0 ];
// 2. 取出省份模型
MJProvince *p = _provinces[pIndex];
// 3. 返回当前省份城市的个数
return p.cities.count;
}
}
#pragma mark - UIPickerView 代理方法
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if (component == 0 ) { // 显示哪个省份
// 1. 取出省份模型
MJProvince *p = _provinces[row];
// 2. 取出省份名称
return p.name;
} else { // 显示哪个城市
// 1. 获得选中了哪一个省
int pIndex = [pickerView selectedRowInComponent: 0 ];
// 2. 取出省份模型
MJProvince *p = _provinces[pIndex];
// 3. 返回对应行的城市名称
return p.cities[row];
}
}
#pragma mark 监听选中了某一列的某一行
- ( void )pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (component == 0 ) { // 改变了省份
// 刷新第 1 列的数据 ( 重新刷新数据,重新调用数据源和代理的相应方法获得数据 )
[pickerView reloadComponent: 1 ];
// 选中第 1 列的第 0 行
[pickerView selectRow: 0 inComponent: 1 animated: YES ];
}
// 更改文字
// 1. 获得选中的省份名称
int pIndex = [pickerView selectedRowInComponent: 0 ];
MJProvince *p = _provinces[pIndex];
// 2. 获得选中的城市位置
int cIndex = [pickerView selectedRowInComponent: 1 ];
// 3. 通知代理
if ([_delegate respondsToSelector: @selector (cityPicker:citySelectWithProvince:city:)]) {
[_delegate cityPicker: self citySelectWithProvince:p.name city:p.cities[cIndex]];
}
}
4.4 调用自定义视图设置城市键盘
设置城市
MJCityPicker *cityPicker = [MJCityPicker cityPicker];
cityPicker.delegate = self ;
_cityField.inputView = cityPicker; // 设置键盘为 pickerview
_cityField.delegate = self ;
#pragma mark - MJCityPicker 代理方法
#pragma mark 选中了某个城市就会调用
- ( void )cityPicker:(MJCityPicker *)cityPicker citySelectWithProvince:(NSString *)province city:(NSString *)city
{
_cityField.text = [NSString stringWithFormat: @"%@ %@" , province, city];
}
4.5 给自定义城市键盘声明一个协议,并添加一个代理属性,当滚动键盘的时候,通知代理做些事情 .
@protocol MJCityPickerDelegate <NSObject]]>
@optional
- ( void )cityPicker:(MJCityPicker *)cityPicker citySelectWithProvince:(NSString *)province city:(NSString *)city;
@end
4.6 在控制器中实现代理协议的方法。
#pragma mark - MJCityPicker 代理方法
#pragma mark 选中了某个城市就会调用
- ( void )cityPicker:(MJCityPicker *)cityPicker citySelectWithProvince:(NSString *)province city:(NSString *)city
{
_cityField.text = [NSString stringWithFormat: @"%@ %@" , province, city];
}
步骤二、自定义 XIB ,用来描述性别
+ ( id )sexBox
{
return [[NSBundle mainBundle] loadNibNamed: @"MJSexBox" owner: nil options: nil ][ 0 ];
}
2.1 监听性别按钮点击,修改按钮的状态
#pragma mark 改变了性别选择
- ( IBAction )sexChange {
if (_manBtn.enabled) { // 点击了男的
_manBtn.enabled = NO ;
_womanBtn.enabled = YES ;
} else { // 点击了女的
_manBtn.enabled = YES ;
_womanBtn.enabled = NO ;
}
}
2.2 .在控制器中 添加性别选择控件
MJSexBox *sexBox = [ MJSexBox sexBox ];
sexBox.center = CGPointMake( 150 , 70 );
[ self .view addSubview:sexBox];
步骤三:自定义键盘
3.1. 设置生日键盘
// 1.1. 生日
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeDate; // 只显示日期 5
datePicker.locale = [[NSLocale alloc] initWithLocaleIdentifier: @"zh_CN" ];
[datePicker addTarget: self action: @selector (birthdayChange:) forControlEvents:UIControlEventValueChanged];
_birthdayField.inputView = datePicker; // 设置键盘为日期选择控件
_birthdayField.delegate = self ;
3.2 禁止生日键盘输入文字
#pragma mark - UITextField 代理
#pragma mark 每当用户输入文字的时候就会调用这个方法,返回 NO ,禁止输入;但会 YES ,允许输入
- ( BOOL )textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
return NO ;
}
3.3 监听生日键盘的值改变事件
#pragma mark - 生日改变
- ( void )birthdayChange:(UIDatePicker *)picker
{
// 1. 取得当前时间
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-dd" ;
NSString *time = [fmt stringFromDate:picker.date];
// 2. 赋值到文本框
_birthdayField.text = time;
}
步骤四、 设置城市
4.1 用 xib 描述城市键盘,并且设置 UIPickerView 的代理和数据源,创建自定义视图。
+ ( id )cityPicker
{
return [[NSBundle mainBundle] loadNibNamed: @"MJCityPicker" owner: nil options: nil ][ 0 ];
}
4.2 加载数据,在 awakeFromNib 中调用
#pragma mark 任何对象从 xib 中创建完毕的时候都会调用一次
- ( void )awakeFromNib
{
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"cities.plist" ofType: nil ]];
_provinces = [NSMutableArray array];
for (NSDictionary *dict in array) {
MJProvince *p = [MJProvince provinceWithDict:dict];
[_provinces addObject:p];
}
}
4.3 实现数据源和代理方法。
#pragma mark - UIPickerView 数据源方法
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2 ;
}
#pragma mark 第 component 列有多少行数据
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if (component == 0 ) { // 多少个省份
return _provinces.count;
} else { // 当前选中省份的行数(城市个数)
// 1. 获得选中了哪一个省
int pIndex = [pickerView selectedRowInComponent: 0 ];
// 2. 取出省份模型
MJProvince *p = _provinces[pIndex];
// 3. 返回当前省份城市的个数
return p.cities.count;
}
}
#pragma mark - UIPickerView 代理方法
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if (component == 0 ) { // 显示哪个省份
// 1. 取出省份模型
MJProvince *p = _provinces[row];
// 2. 取出省份名称
return p.name;
} else { // 显示哪个城市
// 1. 获得选中了哪一个省
int pIndex = [pickerView selectedRowInComponent: 0 ];
// 2. 取出省份模型
MJProvince *p = _provinces[pIndex];
// 3. 返回对应行的城市名称
return p.cities[row];
}
}
#pragma mark 监听选中了某一列的某一行
- ( void )pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (component == 0 ) { // 改变了省份
// 刷新第 1 列的数据 ( 重新刷新数据,重新调用数据源和代理的相应方法获得数据 )
[pickerView reloadComponent: 1 ];
// 选中第 1 列的第 0 行
[pickerView selectRow: 0 inComponent: 1 animated: YES ];
}
// 更改文字
// 1. 获得选中的省份名称
int pIndex = [pickerView selectedRowInComponent: 0 ];
MJProvince *p = _provinces[pIndex];
// 2. 获得选中的城市位置
int cIndex = [pickerView selectedRowInComponent: 1 ];
// 3. 通知代理
if ([_delegate respondsToSelector: @selector (cityPicker:citySelectWithProvince:city:)]) {
[_delegate cityPicker: self citySelectWithProvince:p.name city:p.cities[cIndex]];
}
}
4.4 调用自定义视图设置城市键盘
设置城市
MJCityPicker *cityPicker = [MJCityPicker cityPicker];
cityPicker.delegate = self ;
_cityField.inputView = cityPicker; // 设置键盘为 pickerview
_cityField.delegate = self ;
#pragma mark - MJCityPicker 代理方法
#pragma mark 选中了某个城市就会调用
- ( void )cityPicker:(MJCityPicker *)cityPicker citySelectWithProvince:(NSString *)province city:(NSString *)city
{
_cityField.text = [NSString stringWithFormat: @"%@ %@" , province, city];
}
4.5 给自定义城市键盘声明一个协议,并添加一个代理属性,当滚动键盘的时候,通知代理做些事情 .
@protocol MJCityPickerDelegate <NSObject]]>
@optional
- ( void )cityPicker:(MJCityPicker *)cityPicker citySelectWithProvince:(NSString *)province city:(NSString *)city;
@end
4.6 在控制器中实现代理协议的方法。
#pragma mark - MJCityPicker 代理方法
#pragma mark 选中了某个城市就会调用
- ( void )cityPicker:(MJCityPicker *)cityPicker citySelectWithProvince:(NSString *)province city:(NSString *)city
{
_cityField.text = [NSString stringWithFormat: @"%@ %@" , province, city];
}
步骤五:工具条
5.1 自定义一个 xib 描述工具条
+ ( id )keyboardTool
{
return [[NSBundle mainBundle] loadNibNamed: @"MJKeyboardTool" owner: nil options: nil ][ 0 ];
}
5.2 将 toolbar 用 view 包装起来,目的不让外界修改 toolbar ,因为外界拿到的是 UIView ,而不是 toolbar ,就不能直接获取 toolbar 里面的属性了。
5.1 自定义一个 xib 描述工具条
+ ( id )keyboardTool
{
return [[NSBundle mainBundle] loadNibNamed: @"MJKeyboardTool" owner: nil options: nil ][ 0 ];
}
5.2 将 toolbar 用 view 包装起来,目的不让外界修改 toolbar ,因为外界拿到的是 UIView ,而不是 toolbar ,就不能直接获取 toolbar 里面的属性了。
5.3 自定义一个自定义工具条类,和 xib 绑定。
- ( IBAction )previous; // 上一个
- ( IBAction )next; // 下一个
- ( IBAction )done; // 完成
5.4 定义一个协议,通知代理按钮点击事件
@optional
- ( void )keyboardTool:(MJKeyboardTool *)keyboardTool itemClick:(MJKeyboardToolItemType)itemType;
@end
5.5 定义一个枚举类型,区分按钮
typedef enum {
MJKeyboardToolItemTypePrevious, // 上一个
MJKeyboardToolItemTypeNext, // 下一个
MJKeyboardToolItemTypeDone // 完成
} MJKeyboardToolItemType;
5.6 当点击按钮时,通知代理
#pragma mark 上一个
- ( void )previous
{
// 通知代理(上一个按钮被点击了)
if ([_delegate respondsToSelector: @selector (keyboardTool:itemClick:)]) {
[_delegate keyboardTool: self itemClick:MJKeyboardToolItemTypePrevious];
}
}
#pragma mark 下一个
- ( void )next
{
// 通知代理(下一个按钮被点击了)
if ([_delegate respondsToSelector: @selector (keyboardTool:itemClick:)]) {
[_delegate keyboardTool: self itemClick:MJKeyboardToolItemTypeNext];
}
}
#pragma mark 完成
- ( void )done
{
// 通知代理(完成按钮被点击了)
if ([_delegate respondsToSelector: @selector (keyboardTool:itemClick:)]) {
[_delegate keyboardTool: self itemClick:MJKeyboardToolItemTypeDone];
}
}
5.7 让控制器作为工具条的代理,并实现工具条代理方法
#pragma mark - MJKeyboardTool 代理方法
#pragma mark 点击了工具条上面的按钮就会调用
- ( void )keyboardTool:( SUNKeyboardTool *)keyboardTool itemClick:( SUNKeyboardToolItemType )itemType
{
if (itemType == doneKeyboardToolItemType ) {
[ self . view endEditing : YES ];
} else {
int index = [ _fields indexOfObject : _focusedField ];
if (itemType == previousKeyboardToolItemType ) {
index--;
} else {
index++;
}
// 变成第一响应者
[ _fields [index] becomeFirstResponder ];
}
}
5.8
监听所有文本框的开始编辑,设置所有文本框的代理为控制器
// 3. 获得所有的文本输入框
MJKeyboardTool *tool = [MJKeyboardTool keyboardTool];
tool.delegate = self ;
for (UIView *child in self .view.subviews) {
// 如果是文本输入框,就设置工具条
if ([child isKindOfClass:[UITextField class]]) {
UITextField *field = (UITextField *)child;
field.inputAccessoryView = tool;
}
}
5.9 判断工具条上的按钮是否能点击
#pragma mark - UITextField 代理
#pragma mark - 开始点击文本框的时候调用
- ( void )textFieldDidBeginEditing:( UITextField *)textField
{
_focusedField = textField;
int index = [ _fields indexOfObject : _focusedField ];
_keyboardTool . previousBtm . enabled = (index != 0 );
_keyboardTool . nextBtm . enabled = (index != ( _fields . count - 1 ));
}
5.10 监听系统发出键盘滚动通知
#pragma mark - 监听系统发出的通知
- ( void )addkeyboard
{
NSNotificationCenter *center = [ NSNotificationCenter defaultCenter ];
// 监听键盘将要显示
[center addObserver : self selector : @selector (showKeyboard:) name : UIKeyboardWillShowNotification object : nil ];
// 监听键盘将要隐藏
[center addObserver : self selector : @selector (keyboardWillHide:) name : UIKeyboardWillHideNotification object : nil ];
}
5.11 当键盘挡住文本框将视图往上移
#pragma mark - 监听键盘将要显示
- ( void )showKeyboard:( NSNotification *)noti
{
NSLog ( @"%@" ,noti. userInfo );
// 1. 取得当前聚焦文本框最下面的 Y 值
CGFloat Y = CGRectGetMaxY ( _focusedField . frame );
NSLog ( @"%f" ,Y);
// 2. 取出键盘的高度 ( 控制器的高度 - 键盘的高度 )
CGFloat keyboardH = [noti. userInfo [ UIKeyboardFrameBeginUserInfoKey ] CGRectValue ]. size . height ;
CGFloat keyboardY = self . view . frame . size . height - keyboardH;
// 3. 比较文本框的大小
CGFloat duration = [noti. userInfo [ UIKeyboardAnimationDurationUserInfoKey ] floatValue ];
[ UIView animateWithDuration :duration animations :^{
// 键盘遮住了文本框
if (keyboardY < Y) {
self . view . transform = CGAffineTransformMakeTranslation ( 0 , keyboardY - Y - 10 );
} else {
self . view . transform = CGAffineTransformIdentity ;
}
}];
}
#pragma mark - 监听键盘将要隐藏
- ( void )keyboardWillHide:( NSNotification *)noti
{
// 时长
CGFloat duration = [noti. userInfo [ UIKeyboardAnimationDurationUserInfoKey ] floatValue ];
// 设置动画,让键盘隐藏的时候按照时长隐藏
[ UIView animateWithDuration :duration animations :^{
// CGAffineTransformIdentity 属性能还原到进行动画 之前的状态
self . view . transform = CGAffineTransformIdentity ;
}];
}
5.12 移除监听通知
#pragma mark - 监听器销毁之前取消注册
- ( void )dealloc
{
[[ NSNotificationCenter defaultCenter ] removeObserver : self ];
}
// 3. 获得所有的文本输入框
MJKeyboardTool *tool = [MJKeyboardTool keyboardTool];
tool.delegate = self ;
for (UIView *child in self .view.subviews) {
// 如果是文本输入框,就设置工具条
if ([child isKindOfClass:[UITextField class]]) {
UITextField *field = (UITextField *)child;
field.inputAccessoryView = tool;
}
}
5.9 判断工具条上的按钮是否能点击
#pragma mark - UITextField 代理
#pragma mark - 开始点击文本框的时候调用
- ( void )textFieldDidBeginEditing:( UITextField *)textField
{
_focusedField = textField;
int index = [ _fields indexOfObject : _focusedField ];
_keyboardTool . previousBtm . enabled = (index != 0 );
_keyboardTool . nextBtm . enabled = (index != ( _fields . count - 1 ));
}
5.10 监听系统发出键盘滚动通知
#pragma mark - 监听系统发出的通知
- ( void )addkeyboard
{
NSNotificationCenter *center = [ NSNotificationCenter defaultCenter ];
// 监听键盘将要显示
[center addObserver : self selector : @selector (showKeyboard:) name : UIKeyboardWillShowNotification object : nil ];
// 监听键盘将要隐藏
[center addObserver : self selector : @selector (keyboardWillHide:) name : UIKeyboardWillHideNotification object : nil ];
}
5.11 当键盘挡住文本框将视图往上移
#pragma mark - 监听键盘将要显示
- ( void )showKeyboard:( NSNotification *)noti
{
NSLog ( @"%@" ,noti. userInfo );
// 1. 取得当前聚焦文本框最下面的 Y 值
CGFloat Y = CGRectGetMaxY ( _focusedField . frame );
NSLog ( @"%f" ,Y);
// 2. 取出键盘的高度 ( 控制器的高度 - 键盘的高度 )
CGFloat keyboardH = [noti. userInfo [ UIKeyboardFrameBeginUserInfoKey ] CGRectValue ]. size . height ;
CGFloat keyboardY = self . view . frame . size . height - keyboardH;
// 3. 比较文本框的大小
CGFloat duration = [noti. userInfo [ UIKeyboardAnimationDurationUserInfoKey ] floatValue ];
[ UIView animateWithDuration :duration animations :^{
// 键盘遮住了文本框
if (keyboardY < Y) {
self . view . transform = CGAffineTransformMakeTranslation ( 0 , keyboardY - Y - 10 );
} else {
self . view . transform = CGAffineTransformIdentity ;
}
}];
}
#pragma mark - 监听键盘将要隐藏
- ( void )keyboardWillHide:( NSNotification *)noti
{
// 时长
CGFloat duration = [noti. userInfo [ UIKeyboardAnimationDurationUserInfoKey ] floatValue ];
// 设置动画,让键盘隐藏的时候按照时长隐藏
[ UIView animateWithDuration :duration animations :^{
// CGAffineTransformIdentity 属性能还原到进行动画 之前的状态
self . view . transform = CGAffineTransformIdentity ;
}];
}
5.12 移除监听通知
#pragma mark - 监听器销毁之前取消注册
- ( void )dealloc
{
[[ NSNotificationCenter defaultCenter ] removeObserver : self ];
}