需求
编辑完之后,点一下空白地方,键盘收起,这是一个比较普遍的需求。比如:
点中输入框,输入 -》输入完了,点一下空白的地方,键盘收起 -》点“确认”按钮,完成提交。
收起键盘方法
- 退出第一响应者,这是最常用的方法
[textField resignFirstResponder];
- 退出编辑;从苹果的解释来看,这是专门用来收键盘的;并且是一个扩展,应该是后来看需求多而添加的。
@interface UIView (UITextField)
- (BOOL)endEditing:(BOOL)force; // use to make the view or any subview that is the first responder resign (optionally force)
@end
- 也是退出编辑,只是用上了
keyWindow
,可以离开Controller
,更方便一点。
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
- 直接发送
resignFirstResponder
消息
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
小结: 其实就是退出第一响应者和结束编辑两种方法,后面两种算是灵活应用。
触发方式
只要用户点中输入组件,就可以自动唤起键盘,非常方便;但是收起键盘,就比较麻烦,需要让所有能够唤起键盘的组件都退出第一响应者。对于单个页面,还算可以;如果有很多页面,很多编辑组件,就很烦了。
在具体的输入组件的响应中做收起键盘的动作,可以做到,但是不胜其烦,而已很容易遗漏。所以,要考虑在所有输入组件之外,触发点击事件。这时候,用退出相应者的方法就不是很好。在输入组件之外,却要引用所有的输入组件,感觉很糟糕。所以,采用让
UIView
退出编辑,或者直接发送resignFirstResponder
消息是可选的方法。在大多数情况下,
Controller
是离不开的。其中的self.view
很自然地满足点击屏幕的空白之处这个条件,所以,采用让UIView
退出编辑这个方式是最自然地。self.view
不能触发点击事件。一种方式是将self.view
的class
改为UIController
。这在故事版上是很容易做到的。不过,我们准备将推出键盘这个操作做在基类Controller
中,这种方式就不合适了。在
self.view
上盖一个透明的UIButton
,来接收用户在“屏幕空白处的点击事件”,然后,调用self.view
的endEditing
方法来收键盘。这种方式是可行的。不过这里要注意的是,添加UIButton
之后,frame
要铺满整个self.view
,并且,在z
轴顺序上要放在最底层,不然,盖住屏幕组件就尴尬了。在
self.view
上添加一个点击手势,然后在手势的处理方法上调用self.view
的endEditing
方法来收键盘。这种方式是最贴近“点击屏幕的空白之处”的方式。这也是本文推荐的方式。
注意事项
手势
UITapGestureRecognizer
有个属性cancelsTouchesInView
,默认是YES
。 在self.view
上添加一个点击手势UITapGestureRecognizer
之后,发现界面上的UIButton
什么的点了都没反应了。将cancelsTouchesInView
这个属性设置为NO
就好了。
怎么理解?从字面意思上就是self.view
上的UITapGestureRecognizer
收到点击事件之后,cancelsTouchesInView
这个属性决定是否将点击事件传给子视图。默认是YES
,就是不传,作为self.view
子视图的UIButton
就收不到点击事件了。
iOS 最简单解决事件冲突的思路,深入浅出cancelsTouchesInView属性点了空白处,收起了键盘,这是我们期望的。但是,点了输入组件呢?这个时候,
self.view
的endEditing
方法也会被执行到,这就很尴尬了。解决方法,当然是让self.view
的UITapGestureRecognizer
对于输入组件的点击不响应。
在view上加UITapGestureRecognizer,按钮不触发单击事件的处理方法
参考代码
可以在基类Controller中做收起键盘的工作,最终的效果是:点击输入组件,键盘弹出;点击空白之处,键盘收起。
- 给
self.view
添加手势
- (void)viewDidLoad {
[super viewDidLoad];
// 添加手势,隐藏键盘
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeybord)];
// 让tap事件和其他UIControl事件可以共存
// https://blog.csdn.net/u011644318/article/details/50052359
// tap手势不应该吃掉子窗口事件;默认是YES
tapGesture.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapGesture];
// 设置代理
tapGesture.delegate = self;
}
- 在对应点击处理方法
hideKeybord
中收起键盘,采用结束编辑的方式。
#pragma mark - selector
// 隐藏键盘
- (void)hideKeybord {
[self.view endEditing:YES];
}
- 让基类
Controller
遵循手势代理
@interface KJTBaseViewController ()<UIGestureRecognizerDelegate>
@end
- 在代理函数中,过滤输入组件
#pragma mark - UIGestureRecognizerDelegate
// 隐藏键盘的手势应该过滤掉UIButton,UITextField等事件,不然会造成响应丢失的现象
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// 过滤掉UIButton; UITextField中的小叉叉,其实是一个UIButton,单步跟踪可以看到会走这里
if ([touch.view isKindOfClass:[UIButton class]]) {
return NO;
}
// 过滤掉UITextField
if ([touch.view isKindOfClass:[UITextField class]]) {
return NO;
}
// 过滤掉UITextView
if ([touch.view isKindOfClass:[UITextView class]]) {
return NO;
}
// 过滤掉UISwitch
if ([touch.view isKindOfClass:[UISwitch class]]) {
return NO;
}
// 过滤掉UISegmentedControl
if ([touch.view isKindOfClass:[UISegmentedControl class]]) {
return NO;
}
// 过滤掉UISlider
if ([touch.view isKindOfClass:[UISlider class]]) {
return NO;
}
return YES;
}