扩展iOS 11安全区域以包含键盘

iOS 11中引入的新的安全区域布局指南很好地防止内容在栏下显示,但不包括键盘。 这意味着当键盘显示时,内容仍然隐藏在它后面,这是我正在试图解决的问题。

我的方法是基于收听键盘通知,然后通过additionalSafeAreaInsets调整安全区域。

这是我的代码:

 override func viewDidLoad() { let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) notificationCenter.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) notificationCenter.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } //MARK: - Keyboard extension MyViewController { @objc func keyboardWillShow(notification: NSNotification) { let userInfo = notification.userInfo! let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0) UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded(); } } @objc func keyboardWillHide(notification: NSNotification) { additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded(); } } @objc func keyboardWillChange(notification: NSNotification) { let userInfo = notification.userInfo! let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0) UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded(); } } } 

这个效果很好,因为MyController是一个UIViewControllerUITableView贯穿整个安全区域。 现在,当键盘出现时,底部被推高,使键盘后面没有单元格。

问题是底部酒吧。 我也有一个工具栏在已经包含在安全区域的底部。 因此,将全键盘高度设置为额外的安全区域插入,将桌面视图的底部精确地推到底部杆的高度。 为了使这种方法工作,我必须设置additionalSafeAreaInsets.bottom等于键盘高度减去底部的高度。

问题1:什么是最好的方式来获得当前的安全区的差距? 手动获取工具栏的框架并使用其高度? 或者是否有可能直接从安全区域布局指南中获得差距?

问题2:据推测,底部栏应该可以改变大小,而不需要改变键盘的大小,所以我也应该实现一些方法来监听栏的变化。 这是最好的viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) ? 或其他地方?

谢谢

什么似乎为我工作是计算view.safeAreaLayoutGuide.layoutFrame和键盘框架之间的交集,然后将其高度设置为additionalSafeAreaInsets.bottom ,而不是整个键盘框架高度。 我没有在我的视图控制器的工具栏,但我有一个标签栏,它是正确的。

完整的代码:

 extension UIViewController { func startAvoidingKeyboard() { NotificationCenter.default.addObserver(self, selector: #selector(_onKeyboardFrameWillChangeNotificationReceived(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } func stopAvoidingKeyboard() { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } @objc private func _onKeyboardFrameWillChangeNotificationReceived(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } let keyboardFrameInView = view.convert(keyboardFrame, from: nil) let safeAreaFrame = view.safeAreaLayoutGuide.layoutFrame.insetBy(dx: 0, dy: -additionalSafeAreaInsets.bottom) let intersection = safeAreaFrame.intersection(keyboardFrameInView) let animationDuration: TimeInterval = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 let animationCurveRawNSN = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue let animationCurve = UIViewAnimationOptions(rawValue: animationCurveRaw) UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { self.additionalSafeAreaInsets.bottom = intersection.height self.view.layoutIfNeeded() }, completion: nil) } } 

如果您需要支持IOS11以前的版本,您可以使用Fabio的function,并添加:

 if #available(iOS 11.0, *) { } 

最终解决scheme

 extension UIViewController { func startAvoidingKeyboard() { NotificationCenter.default.addObserver(self, selector: #selector(_onKeyboardFrameWillChangeNotificationReceived(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } func stopAvoidingKeyboard() { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } @objc private func _onKeyboardFrameWillChangeNotificationReceived(_ notification: Notification) { if #available(iOS 11.0, *) { guard let userInfo = notification.userInfo, let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } let keyboardFrameInView = view.convert(keyboardFrame, from: nil) let safeAreaFrame = view.safeAreaLayoutGuide.layoutFrame.insetBy(dx: 0, dy: -additionalSafeAreaInsets.bottom) let intersection = safeAreaFrame.intersection(keyboardFrameInView) let animationDuration: TimeInterval = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 let animationCurveRawNSN = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue let animationCurve = UIViewAnimationOptions(rawValue: animationCurveRaw) UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { self.additionalSafeAreaInsets.bottom = intersection.height self.view.layoutIfNeeded() }, completion: nil) } } }