首页 > 解决方案 > 如何在模态呈现的表单 UIViewController 内为 UIScrollView 计算正确的键盘 contentInset

问题描述

我遇到了一个问题,即依靠convertRect正确报告用于计算 a 的位置contentInset在 iOS 12 上不起作用。这种方法曾经适用于早期的 iO​​S 版本:

@objc func keyboardVisibilityChanged(notification: Notification) {
    guard let userInfo = notification.userInfo else {
        assertionFailure()
        return
    }

    let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let keyboardViewEndFrame = scrollView.convert(keyboardScreenEndFrame, from: view.window!)

    if notification.name == UIResponder.keyboardWillHideNotification {
        scrollView.contentInset = .zero
        scrollView.scrollIndicatorInsets = .zero
    } else {
        let insets = UIEdgeInsets(top: 0, left: 0, bottom: (keyboardViewEndFrame.origin.y - keyboardViewEndFrame.size.height) , right: 0)
        scrollView.contentInset = insets
        scrollView.scrollIndicatorInsets = insets
    }
}

然而,这段代码虽然实现了非常接近的视觉效果,但并不准确,并且在 iPhone 上也会中断,其中模式是全屏显示的。

破碎的插图

标签: iosswiftkeyboarduikit

解决方案


苹果在他们的文档中声明:

注意:包含在 userInfo 字典的 UIKeyboardFrameBeginUserInfoKey 和 UIKeyboardFrameEndUserInfoKey 属性中的矩形应该只用于它包含的大小信息。不要在矩形相交操作中使用矩形的原点(始终为 {0.0, 0.0})。因为键盘是动画到位的,所以键盘的实际边界矩形会随着时间而变化。

所以我想出了以下似乎在 iOS 13、12 和 11 上运行良好的解决方案,包括安全区域、模态表单和硬件键盘):

// MARK: - Keyboard Notifications
@objc func keyboardVisibilityChanged(notification: Notification) {       
    if notification.name == UIResponder.keyboardWillHideNotification {
        scrollView.contentInset = .zero
        scrollView.scrollIndicatorInsets = .zero
    } else {
        guard let userInfo = notification.userInfo,
            let value = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
            let window = view.window else {
                assertionFailure()
                return
        }

        let keyboardEndFrameInWindowCoordinates = value.cgRectValue
        let viewFrameInWindowCoordinates = window.convert(scrollView.frame,
                                                          from: scrollView.superview)

        let contentInsetBottom: CGFloat

        // If the keyboard is below us, no need to do anything.
        // This can happen when a hardware keyboard is attached to a modal form sheet on iPad
        if keyboardEndFrameInWindowCoordinates.origin.y >= viewFrameInWindowCoordinates.maxY {
            contentInsetBottom = 0
        } else {
            let bottomEdgeOfViewInWindowBottomCoordinates = window.frame.maxY - viewFrameInWindowCoordinates.maxY
            contentInsetBottom = keyboardEndFrameInWindowCoordinates.height - bottomEdgeOfViewInWindowBottomCoordinates - view.safeAreaInsets.bottom
        }

        let insets = UIEdgeInsets(top: 0,
                             left: 0,
                             bottom: contentInsetBottom,
                             right: 0)

        scrollView.scrollIndicatorInsets = insets
    }
}

推荐阅读