如何在模态呈现的表单中为 UIScrollView 计算正确的键盘 contentInset sheet UIViewController
How to calculate proper keyboard contentInset for UIScrollView inside of a modally presented form sheet UIViewController
我遇到一个问题,依靠 convertRect
正确报告用于计算 contentInset
的 y 位置在 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 上中断,其中模态全屏显示。
Apple states 在他们的文档中:
Note: The rectangle contained in the UIKeyboardFrameBeginUserInfoKey
and UIKeyboardFrameEndUserInfoKey properties of the userInfo
dictionary should be used only for the size information it contains.
Do not use the origin of the rectangle (which is always {0.0, 0.0}) in
rectangle-intersection operations. Because the keyboard is animated
into position, the actual bounding rectangle of the keyboard changes
over time.
所以我想出了以下似乎在 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
}
}
我遇到一个问题,依靠 convertRect
正确报告用于计算 contentInset
的 y 位置在 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 上中断,其中模态全屏显示。
Apple states 在他们的文档中:
Note: The rectangle contained in the UIKeyboardFrameBeginUserInfoKey and UIKeyboardFrameEndUserInfoKey properties of the userInfo dictionary should be used only for the size information it contains. Do not use the origin of the rectangle (which is always {0.0, 0.0}) in rectangle-intersection operations. Because the keyboard is animated into position, the actual bounding rectangle of the keyboard changes over time.
所以我想出了以下似乎在 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
}
}