欢迎关注
最酷最in的云资讯

iOS 键盘管理器:FJFKeyboardHelper

一. 前言

我们项目中很经常会碰到输入框和键盘的遮挡问题,写多了我们会发现,不同视图和场景下的处理逻辑大同小异,都是先注册监听键盘的弹起和隐藏事件,然后在弹起和隐藏的时候做处理,之后再移除键盘的相关监听,因此你会发现,重复性代码挺多;虽然现在已经有了IQKeyboardManager这样优秀的第三方来管理,但有时候又会觉得IQKeyboardManager过于重量级,功能过于庞大,你可能不想引入这么庞大的第三方,出于这个原因我自己写了一个键盘管理的辅助类,来处理键盘和输入框的遮挡事件,核心代码就一百多行,处理只需要一句话就可以解决。

二.使用介绍

  • 使用方法

/** 移除 键盘 管理器 */+ (void)removeKeyboardHelper;/** 更新 键盘 和 响应者 间距  @param spacing 键盘 和 响应者 间距(默认0.5) */+ (void)updateKeyboardTofirstResponderSpacing:(CGFloat)spacing;/** 处理 containerView 键盘 遮挡  @param containerView 需要移动的视图 */+ (void)handleKeyboardWithContainerView:(UIView *)containerView;/** 处理 scrollView 键盘 遮挡(列表型)  @param scrollView scrollView */+ (void)handleKeyboardWithScrollView:(UIScrollView *)scrollView;/** 处理 键盘  @param showBlock 显示 回调 @param hideBlock 隐藏 回调 */+ (void)handleKeyboardWithShowBlock:(FJFKeyboardManagerBlock)showBlock hideBlock:(FJFKeyboardManagerBlock)hideBlock;

举个例子:

// 视图 调用 [FJFKeyboardHelper handleKeyboardWithContainerView:self.view];// 列表 调用 [FJFKeyboardHelper handleKeyboardWithScrollView:self.tableView];
  • 集成方法:

静态:手动将FJFKeyboardHelper文件夹拖入到工程中。动态:CocoaPods:pod 'FJFKeyboardHelper'
  • github 链接

Demo地址: https://github.com/fangjinfeng/FJFKeyboardHelper

  • 效果展示:

iOS 键盘管理器:FJFKeyboardHelper

三. 原理分析

1. 原理简介

FJFKeyboardHelper有2种可供选择的处理方法:

第一种:FJFKeyboardHelper来处理键盘遮挡

使用者提供键盘遮挡时需要移动的视图containerView,FJFKeyboardHelper通过注册键盘通知,在键盘弹起时,通过获取当前响应者相对于UIWindow的位置,来移动containerView的frame以此来解决键盘遮挡,在键盘隐藏时,将containerView的frame还原。

第二种:使用者通过回调自己处理键盘遮挡

FJFKeyboardHelper的注册键盘通知,在键盘弹起时,通过showBlock回调,在键盘隐藏时,通过hideBlock的回调,由使用者自己来处理键盘遮挡问题。

2. 代码分析:

FJFKeyboardHelper的3个类方法:

第一种:处理普通视图输入框的键盘遮挡

/** 处理 containerView 键盘 遮挡  @param containerView 需要移动的视图 */+ (void)handleKeyboardWithContainerView:(UIView *)containerView;
  • 方法调用 [FJFKeyboardHelper handleKeyboardWithContainerView:self.view];

  • FJFKeyboardHelper的类方法+ (void)handleKeyboardWithContainerView:(UIView *)containerView实现如下:

  FJFKeyboardHelper *helper = [[FJFKeyboardHelper alloc] init];  [helper handleKeyboardWithContainerView:containerView];  [[UIViewController fjf_keyboardCurrentViewController].view fjf_setkeyboardHelper:helper];

这里的helper是个局部变量,为了延长局部变量的生命周期,通过函数fjf_setkeyboardHelper将让当前UIViewController的View强引用helper,防止helper出了作用域就释放。

UIViewController的类方法fjf_keyboardCurrentViewController用来获取当前界面的UIViewController。

UIView的实例方法fjf_setkeyboardHelper用来设置当前view和keyboardHelper的关联。

  • 接着看FJFKeyboardHelper的初始化方法init,

- (instancetype)init {    if (self = [super init]) {                _oldContainerViewFrame = CGRectZero;                [self addKeyboardNotiObserver];    }    return self;}

在init方法这里将oldContainerViewFrame初始化为CGRectZero,同时注册键盘弹起和隐藏的通知事件。

- (void)addKeyboardNotiObserver {    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBoardWillShow:) name:UIKeyboardWillShowNotification object:nil];    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBoardWillHide:) name:UIKeyboardWillHideNotification object:nil];}
  • 接着看helper调用的实例方法handleKeyboardWithContainerView

- (void)handleKeyboardWithContainerView:(UIView *)containerView {    if ([containerView isKindOfClass:[UIView class]]) {        _containerView = containerView;    }        NSAssert([containerView isKindOfClass:[UIView class]], @"containerView 必现是 UIView类型");}

这里只是进行简单的赋值和断言操作,判断当前containerView是否为UIView类型。

  • 最后来看下键盘弹起的回调函数keyBoardWillShow和键盘隐藏的keyBoardWillHide函数:

//  键盘 显示- (void)keyBoardWillShow:(NSNotification *)noti {    if ([noti.name isEqualToString:UIKeyboardWillShowNotification]) {        NSDictionary *keyBordInfo = [noti userInfo];                NSValue *value = [keyBordInfo objectForKey:UIKeyboardFrameEndUserInfoKey];                CGRect keyBoardRect = [value CGRectValue];                CGRect beginRect = [[keyBordInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];                CGRect endRect = [[keyBordInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];                if (CGRectEqualToRect(_oldContainerViewFrame, CGRectZero)) {            _oldContainerViewFrame = _containerView.frame;        }                // 第三方键盘回调三次问题,监听仅执行最后一次        if(beginRect.size.height > 0 && (beginRect.origin.y - endRect.origin.y > 0)){                       // 有回调            if (self.keyboardShowBlock) {                self.keyboardShowBlock(noti.name, noti.userInfo, keyBoardRect);            }            // 无回调            else {                UIView *tmpView = [UIResponder fjf_keyboardCurrentFirstResponder];                if ([tmpView isKindOfClass:[UIView class]]) {                    UIWindow * window = [[[UIApplication sharedApplication] delegate] window];                    CGRect rect = [tmpView convertRect:tmpView.bounds toView:window];                    CGFloat viewBottomHeight =  [UIScreen mainScreen].bounds.size.height - CGRectGetMaxY(rect);                    if (viewBottomHeight  0 ) {                                               // 列表                        if (_scrollView) {                            CGFloat contentOffsetY = self.scrollView.contentOffset.y +  viewBottomOffset;                            [UIView animateWithDuration:durationValue.floatValue animations:^{                                self.scrollView.contentOffset = CGPointMake(0, contentOffsetY);                            }];                        }                        // 非列表                        else if(_containerView){                            CGFloat contentOffsetY = _oldContainerViewFrame.origin.y - viewBottomOffset;                            [UIView animateWithDuration:durationValue.floatValue animations:^{                                self.containerView.frame  = CGRectMake(self.oldContainerViewFrame.origin.x, contentOffsetY, self.oldContainerViewFrame.size.width, self.oldContainerViewFrame.size.height);                            }];                        }                    }                }            }        }    }}

在键盘即将显示的回调函数里面:

a. 我们先判断_oldContainerViewFrame是否为CGRectZero,如果是将_containerView.frame赋值给_oldContainerViewFrame

b. 依据键盘的beginRect和endRect判断出键盘的最后一次回调,因为这里第三方键盘会回调三次,这里只需要执行最后一次即可。

c. 然后判断使用者是否要自己处理键盘遮挡,如果是,则直接通过block回调给使用者,如果不是,先去获取当前响应者,依据响应者算出相对UIWindow的位置,然后算出需要偏移的偏移量。

该函数[UIResponder fjf_keyboardCurrentFirstResponder];主要用来获取当前的第一响应者。

d. 接着判断当前需要移动的视图是否为scrollView类型,如果是scrollView类型就通过设置contentOffset来偏移,如果不是,就通过设置frame来偏移。

// 键盘 隐藏- (void)keyBoardWillHide:(NSNotification *)noti {    if ([noti.name isEqualToString:UIKeyboardWillHideNotification]) {        NSDictionary *keyBordInfo = [noti userInfo];                NSValue *value = [keyBordInfo objectForKey:UIKeyboardFrameEndUserInfoKey];                CGRect keyBoardRect = [value CGRectValue];        // 有回调        if (self.keyboardHideBlock) {            self.keyboardHideBlock(noti.name, noti.userInfo, keyBoardRect);        }        // 无回调        else {            // 非列表             NSString *durationValue = [keyBordInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];            if(_containerView){                [UIView animateWithDuration:durationValue.floatValue animations:^{                    self.containerView.frame  = self.oldContainerViewFrame;                    self.oldContainerViewFrame = CGRectZero;                }];            }        }    }}

在键盘即将隐藏的回调函数里面:

  • 我们首先判断使用者是否设置回调来自己处理键盘隐藏事件,如果有回调,则直接通过block回调给使用者,如果没有,再判断当前需要移动视图是否为非scrollView类型,如果是非scrollView类型,就将之前存储的oldContainerViewFrame赋值给self.containerView的frame,并将self.oldContainerViewFrame置为CGRectZero。

这里之所以在键盘隐藏时候将self.oldContainerViewFrame置为CGRectZero,是为了保证获取到需要移动视图的最新的frame,以防止中间过程中可能需要移动的视图的frame会被修改,而FJFKeyboardHelper的实例里面却不是最新的。

第二种:处理列表输入框的键盘遮挡

/** 处理 scrollView 键盘 遮挡(列表型)  @param scrollView scrollView */+ (void)handleKeyboardWithScrollView:(UIScrollView *)scrollView;

这个方法跟第一个处理普通视图输入框的键盘遮挡方法差不多,唯一的区别就是键盘即将显示的时候,列表是通过设置contentOffset来进行偏移,普通视图是通过设置frame来进行偏移。

第三种:使用者自己处理键盘回调

/** 处理 键盘  @param showBlock 显示 回调 @param hideBlock 隐藏 回调 */+ (void)handleKeyboardWithShowBlock:(MOAKeyboardManagerBlock)showBlock hideBlock:(MOAKeyboardManagerBlock)hideBlock;

这个方法的处理是相对比较简单的,就是在键盘即将显示和键盘即将隐藏的回调函数里面,通过block回调给使用者自己处理。

四. 总结

综上所述就是FJFKeyboardHelper这个键盘管理器的一个设计思路,核心代码量也就一百来行,能处理大部分的键盘遮挡场景,而且只需要一句代码。

如果你觉得你觉得这思路或是代码有什么问题,欢迎留言大家讨论下!如果觉得不错,麻烦给个喜欢或star,谢谢!

作者:林大鹏天地

链接:https://www.swifty.cc/p/34b50c7a111f

赞(0) 打赏
未经允许不得转载:云微资讯 » iOS 键盘管理器:FJFKeyboardHelper
分享到: 更多 (0)

云微资讯 科技新媒体资讯平台

关于我们联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏