对于存在输入框的界面,有时候会出现键盘遮挡输入框的问题。如果被遮挡的输入框恰好是第一响应者或者说是用户正在往其中输入内容的框,那么就可能导致用户看不到或者看不全已经输入的内容,自然会严重影响用户体验。下面就来说说如何解决这一BUG。
第一种方法:针对含有输入框并且输入框有可能被键盘遮挡的页面使用UIScrollView视图。
我们可以把输入框所在的父视图(里面可能还包含一些UIButton、UILabel等视图)添加到UIScrollView视图中,监听键盘事件。当键盘弹出时,判断键盘是否遮挡当前输入框(第二种方法中会给出判断的代码),再根据遮挡的范围,更改输入框父视图在UIScrollView视图的y坐标,同时更新UIScrollView视图的contentSize以及contentOffset.y值,形成向上滑动的效果,从而使输入框离开键盘的遮挡区域。待键盘退出时,恢复此前的设置。
第二种方法:封装一个UIViewController子类,让含有输入框的视图控制器继承之。
上面的方法虽然有效,但很难做到一通百通的效果,为此我们应该考虑封装一个视图控制器类,凡是含有输入框的视图控制器只要继承了它就能自动实现当键盘遮挡输入框时,视图整体上移的效果。具体实现如下:
我们创建一个UIViewController子类ViewController,并声明两个变量
@interface ViewController () {
CGFloat _totalYOffset;
}
@end
在ViewController的初始化话方法中给两个变量赋值
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_totalYOffset = 0;
}
return self;
}
在viewDidAppear:和viewWillDisappear:方法中分别注册键盘事件监听和移除键盘事件监听
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidChangeFrameNotification object:nil];
}
有些键盘事件我们可能用不到,但这里一并注册了。
下面就是两个关键方法的实现
- (void)keyboardWillShow:(NSNotification *)noti
{
CGFloat keyboardHeight = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;;
[self.view.layer removeAllAnimations];
//获取当前第一响应者
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponderView = [keyWindow performSelector:@selector(findFirstResponder)];
//将当前第一响应者的frame转换成应用窗口坐标下的frame,便于跟键盘的frame做比较
CGRect rect = [[UIApplication sharedApplication].keyWindow convertRect:firstResponderView.frame fromView:firstResponderView.superview];
CGFloat bottom = rect.origin.y + rect.size.height;
CGFloat keyboardY = self.view.window.size.height - keyboardHeight;
if (bottom > keyboardY) {
_totalYOffset += bottom - (self.view.window.size.height - keyboardHeight);
[UIView animateWithDuration:[noti.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]
delay:0
options:[noti.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]
animations:^{
CGRect frame = self.view.frame;
frame.origin.y -= _totalYOffset;
self.view.frame = frame;
}
completion:nil];
}
}
- (void)keyboardWillHide:(NSNotification *)noti
{
[UIView animateWithDuration:[noti.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]
delay:0
options:[noti.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]
animations:^{
CGRect frame = self.view.frame;
frame.origin.y += _totalYOffset;
self.view.frame = frame;
}
completion:nil];
_totalYOffset = 0;
}
在此我们还需要对UIView进行Category扩展,其UIView+FindFirstResponder.h文件代码如下
@interface UIView(FindFirstResponder)
- (UIView *)findFirstResponder;
@end
UIView+FindFirstResponder.m文件代码如下
@implementation UIView(FindFirstResponder)
- (UIView *)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}
for (UIView *subView in self.subviews) {
UIView *responder = [subView findFirstResponder];
if (responder) return responder;
}
return nil;
}
@end
ok,顺利解决。
上述代码并不复杂,在此不再累述。
我已将demo上传到了GitHub和CocoaChina上,欢迎大家下载使用。如有什么问题,还望大家不吝赐教。
https://github.com/ahu209/SCAutoMove
http://code.cocoachina.com/view/128397