美文网首页iOS常用
iOS键盘处理(第三方&键盘通知)

iOS键盘处理(第三方&键盘通知)

作者: 苦笑男神 | 来源:发表于2020-06-25 00:33 被阅读0次

    1.前言

    关于键盘,很多时候都是用的IQKeyboardManager这个第三方库,但是有时候一些页面可能真需要自己特殊处理一下。

    https://github.com/hackiftekhar/IQKeyboardManager

    这里记录一下今天处理键盘的过程,遇到的问题有:

    • 第三方键盘弹出时,有多次通知,且获取键盘的Frame都在改变
    • 输入控件有inputAccessoryView时,会再多一次通知
    • UIViewtransform属性,可能会改变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。
    我是小侯爷。
    在帝都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
    如果读完觉得有收获的话,记得关注和点赞哦。
    非要打赏的话,我也是不会拒绝的。

    相关文章

      网友评论

        本文标题:iOS键盘处理(第三方&键盘通知)

        本文链接:https://www.haomeiwen.com/subject/wucjfktx.html