IQKeyboardManager 简单使用

IQKeyboardManager是一个自动处理键盘弹出、隐藏的三方库,使用非常方便,只需要将库引入工程即可使用,不需要做任何多余的设置,我们在项目中也不需要再单独去监听键盘的willShow和willHide了。

IQKeyboardManager类.png

IQKeyboardManager发展到今天已经很成熟了,常见的Bug基本已经解决了。我们今天就简单的记录下IQKeyboardManager的使用和实现原理。

IQKeyboardManager属性含义记录

首先我们先简单记录下常用的属性设置含义,方便以后查询

// 设置键盘自动弹起
[self setEnable:YES];
// 设置键盘和输入框之间的距离
[self setKeyboardDistanceFromTextField:10.0];
// 当键盘弹起时,点击背景,键盘就会收回
[self setShouldResignOnTouchOutside:YES];
// 是否添加键盘上的toolBar
[self setEnableAutoToolbar:YES];
// 是否在toolBar显示输入框中的placeHoder
[self setShouldShowTextFieldPlaceholder:YES];
// 有多个输入框时,可以通过点击Toolbar 上的“前一个”“后一个”按钮来实现移动到不同的输入框
 [self setToolbarManageBehaviour:IQAutoToolbarBySubviews];
// 设置toolBar上的颜色文字颜色是否用户自定义
[self setShouldToolbarUsesTextFieldTintColor:NO];

如果在整个项目中引入了IQKeyboardManager,而某一个UIViewController内不使用IQKeyboardManager,可以如下设置:

 - (void) viewWillAppear: (BOOL)animated 
{
        //打开键盘事件相应
        [IQKeyboardManager sharedManager].enable = NO;
}
- (void) viewWillDisappear: (BOOL)animated
{
       //关闭键盘事件相应
       [IQKeyboardManager sharedManager].enable = YES;
}

这样我们就可以在这个UIViewController中自己添加代码监听系统的键盘通知。

如果在某个页面,有多个TextField,而只有某一个TextField不需要IQ键盘上面的toolBar时,只需要设置下其inputAccessoryView

// 如果需要针对某个textField,不需要键盘上的toolBar 可以单独设置
textField.inputAccessoryView = [[UIView alloc] init];

IQKeyboardManager实现原理

我们先观察下IQKeyboardManager的类文件,就会发现核心代码都在IQKeyboardManager.m这个类中,其他的类文件,基本是辅助类。

在IQKeyboardManager.m的init方法中,我们看到它添加了键盘的弹出和消失通知,并且注册了textField和textView的开始编辑和结束编辑的通知

// 键盘将要弹出的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
// 键盘将要隐藏的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
// UITextField开始编辑和结束编辑的通知
[self registerTextFieldViewClass:[UITextField class]
didBeginEditingNotificationName:UITextFieldTextDidBeginEditingNotification didEndEditingNotificationName:UITextFieldTextDidEndEditingNotification];
// UITextView开始编辑和结束编辑的通知
[self registerTextFieldViewClass:[UITextView class]           didBeginEditingNotificationName:UITextViewTextDidBeginEditingNotification         didEndEditingNotificationName:UITextViewTextDidEndEditingNotification];

正是添加了这4个方法,IQKeyboardManager监听到了一系列通知,并能在-(void)textFieldViewDidBeginEditing:(NSNotification*)notification方法中记录下是哪个控件要开始编辑,并需要键盘弹起
记录输入源.png

通过记录下这个_textFieldView,IQKeyboardManager知道了输入源原来的坐标值,PlaceHolder等值并一一记录下来,方便在键盘发出willShow通知时去调整控件的坐标。

也正是由于知道了这个输入源_textFieldView,IQKeyboardManager才能在-(void)keyboardWillShow:(NSNotification*)aNotification方法中,

  1. _textFieldView 直接被添加到UIViewController中

借用响应链原理的具体应用[_textFieldView viewController]方法找到_textFieldView被添加进了哪个UIViewController中

// 确定_textFieldView的下一个UIResponder是否为UIViewController,如果是UIViewController类,则返回
-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;
    do
    {
        nextResponder = [nextResponder nextResponder];
        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;
    } while (nextResponder != nil);
    return nil;
}

并通过topMostController确定该UIViewController是否是真的为_textFieldView的所在的VC 。

-(UIViewController *)topMostController
{
    NSMutableArray *controllersHierarchy = [[NSMutableArray alloc] init];
    
    UIViewController *topController = self.window.rootViewController;
    
    if (topController)
    {
        [controllersHierarchy addObject:topController];
    }
    
    while ([topController presentedViewController])
    {
        
        topController = [topController presentedViewController];
        [controllersHierarchy addObject:topController];
    }
    
    UIResponder *matchController = [self viewController];
    
    while (matchController != nil && [controllersHierarchy containsObject:matchController] == NO)
    {
        do
        {
            matchController = [matchController nextResponder];
            
        } while (matchController != nil && [matchController isKindOfClass:[UIViewController class]] == NO);
    }
    
    return (UIViewController*)matchController;
}

如果确定了_textFieldView 只是直接被添加到UIViewController上,则rootViewRect记录下该UIViewController原始坐标,如果有需要则改变rootViewRect的坐标值,并在键盘消失时恢复该UIViewController到原始坐标。

{
            //  +Positive or zero.
            if (move>=0)
            {
                rootViewRect.origin.y -= move;
                
                //  From now prevent keyboard manager to slide up the rootView to more than keyboard height. (Bug ID: #93)
                if (_preventShowingBottomBlankSpace == YES)
                {
                    rootViewRect.origin.y = MAX(rootViewRect.origin.y, MIN(0, -kbSize.height+keyboardDistanceFromTextField));
                }
                
                [self showLog:@"Moving Upward"];
                //  Setting adjusted rootViewRect
                [self setRootViewFrame:rootViewRect];
                _movedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y);
            }
            //  -Negative
            else
            {
                CGFloat disturbDistance = CGRectGetMinY(rootViewRect)-CGRectGetMinY(_topViewBeginRect);
                
                //  disturbDistance Negative = frame disturbed. Pull Request #3
                //  disturbDistance positive = frame not disturbed.
                if(disturbDistance<0)
                {
                    rootViewRect.origin.y -= MAX(move, disturbDistance);
                    
                    [self showLog:@"Moving Downward"];
                    //  Setting adjusted rootViewRect
                    [self setRootViewFrame:rootViewRect];
                    _movedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y);
                }
            }
        }

如果确定了_textFieldView 只是直接被添加到UIScrollView上,则
UIScrollView superView = (UIScrollView)[_textFieldView superviewOfClassType:[UIScrollView class]];记录该UIScrollView为_lastScrollView

else if(superScrollView)
    {
        _lastScrollView = superScrollView;
        _startingContentInsets = superScrollView.contentInset;
        _startingContentOffset = superScrollView.contentOffset;
        _startingScrollIndicatorInsets = superScrollView.scrollIndicatorInsets;
    }

利用UIScrollView的偏移量去主动滑动

superScrollView.contentOffset = CGPointMake(superScrollView.contentOffset.x, shouldOffsetY);

在键盘将要消失时,由于我们已经存储的_lastScrollView就是添加_textFieldView的那个UIScrollView,所以就能恢复原始位置了,这样就实现了_textFieldView随键盘的弹出和消失而自动调整了。

这里只是简单的分析了下输入控件是UITextField,其被添加到UIScrollView 和 UIViewController中的场景,其他场景实现原理基本类似,就不在写了。

如果你喜欢我的创作,请点赞! 你的赞是我继续创作的动力,谢谢!如果不喜欢,请留言,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容