ios - 如何在模态呈现的表单 UIViewController 内为 UIScrollView 计算正确的键盘 contentInset
问题描述
我遇到了一个问题,即依靠convertRect
正确报告用于计算 a 的位置contentInset
在 iOS 12 上不起作用。这种方法曾经适用于早期的 iOS 版本:
@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 上也会中断,其中模式是全屏显示的。
解决方案
苹果在他们的文档中声明:
注意:包含在 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
}
}
推荐阅读
- azure - 尝试向 Azure 订阅添加其他所有者时的奇怪现象
- swift - 使用 Charts API Swift 显示时间戳
- angular - RouterModule.forRoot() 再次调用了两次
- angular - 日期验证,以便不选择下一个日期
- reactjs - 如果语句在 componentDidUpdate 的开玩笑测试中失败
- r - 在 R 中读取一个非常小的 p 值
- java - 打印 100 到 1000 之间的素数,其数字总和等于 19
- python - Python - 如何从第 0 个元素而不是第 x 个元素循环数组?
- google-sheets - Google Sheet - 一个接一个地放置 3 个动态列(原始更改的数量)以创建一个列表
- google-apps-script - 替换特定文件夹的多个 Google 文档中的文本