为 keyboardWillShowNotification 设置 UIScrollView 的 contentInset 不能正常工作

Posted

技术标签:

【中文标题】为 keyboardWillShowNotification 设置 UIScrollView 的 contentInset 不能正常工作【英文标题】:Setting UIScrollView's contentInset for keyboardWillShowNotification not working properly 【发布时间】:2019-11-29 10:23:36 【问题描述】:

我有一个 ViewController 具有以下结构((x) 表示级别):

UIViewController    (1)
  - NavigationBar   (2)
  - UIScrollView    (2)
    - UIView        (3)
      - UITextField (4)
      - UITextField (4)
      - UITextField (4)
      - UITextField (4)
      - UIButton    (4)
4 级别的所有元素在垂直方向上相互限制,间距为 16。 4 级别的第一个和最后一个元素被限制在 UIView 的 (3) 顶部和底部。 UIView (3) 的顶部和底部被限制为 UIScrollView (2)。 UIScrollView (2) 被限制在 NavigationBar 的底部 (2) 和 superview 的底部 (1) (当然还有必要的水平约束!)

UIView (3) 有以下约束:

所有子视图的前导约束为 0。 所有子视图的尾随约束为 0。 UIButton 的底部间距为 24(应添加 一些 额外间距) 24 的顶部空间到最顶部的 UITextField(顶部间距) 0 到 superView (UIScrollView) 的顶部空间 0到superView(UIScrollView)的底部空间 '等宽 - 负 32' 到 NavigationBar(所以 - 固定宽度)

在viewController的viewDidLoad中,我称之为:

registerForKeyboardWillShowNotification(self.scrollView)
registerForKeyboardWillHideNotification(self.scrollView)

其中registerForKeyboard...ShowNotificationUIViewController 的扩展:

extension UIViewController

    /// Act when keyboard is shown, by adding contentInsets to the scrollView.
    func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
    
        _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification,
                                                   object: nil, queue: nil)
         notification in
            let userInfo      = notification.userInfo!
            let keyboardSize  = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
            let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
                                             left: scrollView.contentInset.left,
                                             bottom: keyboardSize.height,
                                             right: scrollView.contentInset.right)

            scrollView.contentInset = contentInsets
            block?(keyboardSize)
        
    

    /// Act when keyboard is hidden, by removing contentInsets from the scrollView.
    func registerForKeyboardWillHideNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
    
        _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification,
                                                   object: nil, queue: nil)
         notification in
            let userInfo = notification.userInfo!
            let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
            let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
                                             left: scrollView.contentInset.left,
                                             bottom: 0,
                                             right: scrollView.contentInset.right)

            scrollView.contentInset = contentInsets
            block?(keyboardSize)
        
    

但是,当键盘显示时,它并没有插入 scrollView(够了)。我调试了一下,是这样的:

普通键盘:height = 216 带建议栏的常规键盘:height = 260 带建议栏的 iPhone X 键盘:height = 291

我首先虽然建议栏可能是问题,但事实并非如此。

registerForKeyboardWillShowNotification 中,我将bottom: keyboardSize.height 更改为bottom: keyboardSize.height + 30,结果完全相同(我看到按钮的相同部分部分隐藏在键盘后面)。一旦我添加了 50 个或更多,它最终似乎会产生微小的差异。

我尝试了keyboardDidShowNotification 而不是keyboardWillShowNotification,但这并没有什么不同。 我尝试了keyboardFrameBeginUserInfoKey 而不是keyboardFrameEndUserInfoKey,但这并没有什么不同。

我在这里错过了什么?

【问题讨论】:

你的 UIView 约束是什么? 我编辑了问题并添加了 UIView 约束。 UIView 的高度呢? 这是从它的内容(TextFields / Button)派生的。因为它对那些有 24 的顶部和底部约束。 首先解决约束错误,我认为这是主要问题 【参考方案1】:

很遗憾,我无法解决这个问题,我不确定为什么它不能按预期工作。

但是,通过在 UIScrollView 中使用 UIStackView,我得到了预期的行为。我使用this article 作为参考。


界面布局

UIViewController (1) 导航栏 (2) UIScrollView (2) UIStackView (3) UITextField (4) UITextField (4) UITextField (4) UITextField (4) UIButton (4)

UIScrollView

ScrollView 的leadingtrailing16 约束到superView。 ScrollView 的 top0 限制在导航栏的底部。 ScrollView 的 bottom0 约束到 superView 的底部。

UIStackView

StackView 的 leadingtrailing0 约束到滚动视图。 StackView 的宽度与 scrollView 相同。 StackView 的 topbottom24 约束到滚动视图,以获得所需的导航栏间距以及按钮和键盘之间的间距。 StackView 设置为axis=verticalalignment=filldistribution=fillspacing=24

导航栏

NavigationBar 是一个自定义类,它从其内容派生其高度。这没有设置高度约束,但占位符高度为 100。NavigationBar 将填满整个屏幕。这可以通过删除占位符高度并添加 any 低优先级的高度约束(在本例中为1)来解决。


应用键盘插入的初始代码现在可以工作了。

/// Act when keyboard is shown, by adding contentInsets to the scrollView.
func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)

    _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification,
                                               object: nil, queue: nil)
     notification in
        let userInfo      = notification.userInfo!
        let keyboardSize  = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
        let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
                                         left: scrollView.contentInset.left,
                                         bottom: keyboardSize.height,
                                         right: scrollView.contentInset.right)

        scrollView.contentInset = contentInsets
        block?(keyboardSize)
    

【讨论】:

以上是关于为 keyboardWillShowNotification 设置 UIScrollView 的 contentInset 不能正常工作的主要内容,如果未能解决你的问题,请参考以下文章

为啥每次选择另一个 TextField 时都会调用 KeyboardWillShowNotification?

KeyboardWillShowNotification 不适用于 iOS > 6.1

ios 监控键盘状态

将 A 转换为 1 B 转换为 2 ... Z 转换为 26,然后将 AA 转换为 27 AB 转换为 28(Excel 中列引用的列索引)

考试错题

SQL判断字段是不是为空,为NULL