解决UIKeyboard的问题看我就够了

2,892 阅读2分钟

我们在iOS的开发过程中,经常要在包含UIScrollView的view中监听键盘的通知,有时候会出现一些bug,比方说UIKeyboardWillShowNotification可能会调用多次。

通过这篇文章我们来讨论几个经常出现的问题,并且了解一下对应的解决方案。

到公众号【iOS开发栈】学习更多SwiftUI、iOS开发相关内容。

UIScrollView退出键盘

拖动UIScrollView退出键盘的方法:

typedef NS_ENUM(NSInteger, UIScrollViewKeyboardDismissMode) {
    UIScrollViewKeyboardDismissModeNone,
    UIScrollViewKeyboardDismissModeOnDrag,      // dismisses the keyboard when a drag begins
    UIScrollViewKeyboardDismissModeInteractive, // the keyboard follows the dragging touch off screen, and may be pulled upward again to cancel the dismiss
}

@property(nonatomic) UIScrollViewKeyboardDismissMode keyboardDismissMode // 默认是None,UIScrollView和Keyboar不会有任何交互效果
  • UIScrollViewKeyboardDismissModeOnDrag 当开始拖动时,keyboard会退出屏幕。在iOS14的模拟器上动画有点不流畅。
  • UIScrollViewKeyboardDismissModeInteractive 当拖动手势进入键盘区域时,键盘会跟随拖动上升或者下降。

UIKeyboardWillShowNotification多次重复通知

多个TextField或者TextView之间切换时,UIKeyboardWillShowNotificationUIKeyboardWillChangeFrameNotification每次都会调用(但是UIKeyboardWillHideNotification却不会调用),当你使用上面提到的UIScrollViewKeyboardDismissModeInteractive时,通知甚至会在一个动画过程中频繁调用。

我们在处理一些业务逻辑的时候需要谨记这个特点,不要寄希望于只会调用一次。比方说要在UIKeyboardWillShowNotification的回调中把某个视图上移。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willShow:) name:UIKeyboardWillShowNotification object:nil]; // 订阅通知

❌
- (void)willShow:(NSNotification *)noti {
    NSLog(@"willShow");
    CGRect keyboarFrame = [noti.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect bottomViewFrame = _bottomView.frame;
    bottomViewFrame.origin.y -= keyboarFrame.size.height;
    _bottomView.frame = bottomViewFrame;
}

✅
// 使用固定高度的方法
- (void)willShow:(NSNotification *)noti {
    NSLog(@"willShow");
    CGRect keyboarFrame = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect bottomViewFrame = _bottomView.frame;
    bottomViewFrame.origin.y = self.view.bounds.size.height - keyboarFrame.size.height - _bottomView.bounds.size.height;
    _bottomView.frame = bottomViewFrame;
}

✅
// 使用全局变量的方法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didHide:) name:UIKeyboardDidHideNotification object:nil]; // 订阅键盘消失的通知

static BOOL keyboarShow = NO;

- (void)willShow:(NSNotification *)noti {
    NSLog(@"willShow");
    if (keyboarShow == YES) {
        return;
    }
    keyboarShow = YES;
    CGRect keyboarFrame = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect bottomViewFrame = _bottomView.frame;
    bottomViewFrame.origin.y = self.view.bounds.size.height - keyboarFrame.size.height - _bottomView.bounds.size.height;
    _bottomView.frame = bottomViewFrame;
}

- (void)didHide:(NSNotification *)noti {
    NSLog(@"didHide");
    keyboarShow = NO;
}

点击UIScrollView触发UIKeyboardWillShowNotification通知

点击TextFiled或者TextView后,再点击UIScrollView,UIKeyboardWillShowNotification的回调会被触发,我不能确定造成这个现象的原因是什么,不过猜测是因为iOS的事件管理系统的内部bug导致的。解决这个问题的终极方法和我们上面⬆️用的方法是一样的。

到公众号【iOS开发栈】学习更多SwiftUI、iOS开发相关内容。

文章首发在我的个人技术博客