1.前言
关于键盘,很多时候都是用的IQKeyboardManager
这个第三方库,但是有时候一些页面可能真需要自己特殊处理一下。
这里记录一下今天处理键盘的过程,遇到的问题有:
- 第三方键盘弹出时,有多次通知,且获取键盘的
Frame
都在改变 - 输入控件有
inputAccessoryView
时,会再多一次通知 -
UIView
的transform
属性,可能会改变View的frame
- 输入控件有
inputAccessoryView
时,原生键盘也会多一次通知 - 弹出键盘后,第三方键盘和原生键盘可以自由切换的,高度可能在切换时变化
- 页面第一次初始化后,第一次获得焦点弹键盘 和关掉键盘,再主动点击输入焦点的键盘通知,也不一样。
2.代码研究
首先.h
所需的一个属性
/// 当是第三方键盘时,用于通知的计数 (第三方键盘在弹出时,会调用多次通知)
@property (nonatomic ,assign) NSInteger KeyboardIndex;
/// 输入控件
@property (nonatomic, weak) UITextView *textView;
注册键盘的通知
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardAction:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardAction:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
键盘的通知代码:
- (void)keyboardAction:(NSNotification *)notification {
NSTimeInterval duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
duration = duration == 0 ? 0.3 : duration;
__weak __typeof(&*self)weakSelf = self;
// 键盘将要弹出
if ([notification.name isEqual:UIKeyboardWillShowNotification]) {
CGRect frame = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// 不是第三方键盘,第三方键盘第一次通知里面的frame里面的height是0
if (frame.size.height > 0 && self.KeyboardIndex == 0) {
[self keyboardWillShow:frame duration:duration];
return;
}
self.KeyboardIndex++;
NSInteger count = 3; // 因为`KeyboardIndex`从0开始的,所以这里其实是 4 次通知
if (self.textView.inputAccessoryView == nil) {
count = 2; // 因为`KeyboardIndex`从0开始的,所以这里其实是 3 次通知
}
// 第三方键盘,会有多次`UIKeyboardWillShowNotification`,只用最后一次
if (self.KeyboardIndex <= count) return;
[self keyboardWillShow:frame duration:duration];
self.KeyboardIndex = 0;
}
if ([notification.name isEqual:UIKeyboardWillHideNotification]) {
// 键盘将要收回,这想加不加判断都一样,因为Translation(0, 0) 本身就没有发生transform
[UIView animateWithDuration:duration animations:^{
weakSelf.contentView.transform = CGAffineTransformMakeTranslation(0, 0);
}];
}
}
上面调用了一个方法[self keyboardWillShow:duration:]
代码是:
/// View在做`transform`动画,或者改变`transform`时,Frame值会跟着改变.
static CGRect staticFrame;
/// 键盘弹出
- (void)keyboardWillShow:(CGRect)keyboardFrame duration:(NSTimeInterval)duration {
if (CGRectIsEmpty(staticFrame) || CGRectIsNaN(staticFrame) || CGRectIsNull(staticFrame)) {
staticFrame = self.contentView.frame;
}
// 加10是为了有5.0个距离差,不想加也可以的。
CGFloat maxY = CGRectGetMaxY(staticFrame) + 5.0;
if (maxY < keyboardFrame.origin.y) return;
CGFloat h = maxY - keyboardFrame.origin.y;
[self.contentView.layer removeAllAnimations]; // 原生键盘也会有多次通知的情况,这里重置状态,方法可能会执行多次
self.contentView.transform = CGAffineTransformIdentity;
[UIView animateWithDuration:duration animations:^{
self.contentView.transform = CGAffineTransformMakeTranslation(0, -h);
}];
}
3.偷懒的可能性
上面的代码看着都有点头疼,那么多情况,不同的机型&键盘&iOS系统,谁知道会不会有特殊情况,所以,如果你不介意页面可能
的跳动,直接copy
下面这种方式就行:
键盘的通知代码:
- (void)keyboardAction:(NSNotification *)notification {
NSTimeInterval duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
duration = duration == 0 ? 0.3 : duration;
__weak __typeof(&*self)weakSelf = self;
if ([notification.name isEqual:UIKeyboardWillShowNotification]) {
CGRect frame = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
[self keyboardWillShow:frame duration:duration];
return;
}
if ([notification.name isEqual:UIKeyboardWillHideNotification]) {
// 键盘将要收回,这想加不加判断都一样,因为Translation(0, 0) 本身就没有发生transform
[UIView animateWithDuration:duration animations:^{
weakSelf.contentView.transform = CGAffineTransformMakeTranslation(0, 0);
}];
}
}
/// View在做`transform`动画,或者改变`transform`时,Frame值会跟着改变.
static CGRect staticFrame;
/// 键盘弹出
- (void)keyboardWillShow:(CGRect)keyboardFrame duration:(NSTimeInterval)duration {
if (CGRectIsEmpty(staticFrame) || CGRectIsNaN(staticFrame) || CGRectIsNull(staticFrame)) {
staticFrame = self.contentView.frame;
}
// 加10是为了有5.0个距离差,不想加也可以的。
CGFloat maxY = CGRectGetMaxY(staticFrame) + 5.0;
if (maxY < keyboardFrame.origin.y) return;
CGFloat h = maxY - keyboardFrame.origin.y;
[self.contentView.layer removeAllAnimations]; // 键盘有多次通知的情况,这里重置状态,方法可能会执行多次
self.contentView.transform = CGAffineTransformIdentity;
[UIView animateWithDuration:duration animations:^{
self.contentView.transform = CGAffineTransformMakeTranslation(0, -h);
}];
}
-- End ---
PS:最近我有跳槽的想法,有工作机会的老板,欢迎骚扰哦!北京呦!
END。
我是小侯爷。
在帝都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。