2017-07-30 67 views
5

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(); 
     } 
    } 
} 

這個工程還有MyControllerUIViewController與通過整個安全區延伸UITableView。現在,當鍵盤出現時,底部被向上推,使得鍵盤後面沒有單元格。

問題在於底部酒吧。我在底部還有一個已經包含在安全區域的工具欄。因此,將全鍵盤高度設置爲額外的安全區域插入,將桌面視圖的底部向上推到底部杆的高度。要使此方法正常工作,我必須將additionalSafeAreaInsets.bottom設置爲等於鍵盤高度減去底部條的高度。

問題1:在底部獲得當前安全區域缺口的最佳方法是什麼?手動獲取工具欄的框架並使用其高度?或者是否有可能直接從安全區域佈局指南中獲得差距?

問題2:假設底部欄應該可以改變大小,而不用更改鍵盤大小,所以我還應該實現一些方法來監聽欄中的變化。這最好在viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)完成嗎?或者其他地方

謝謝

回答

6

什麼,似乎是爲我工作是計算和鍵盤邊框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) 
    } 
} 
+0

這是一個更好的解決方案來應對iPhoneX,因爲它的高度包括一個在安全區外 –

+0

...但不幸的是它使用safeAreaLayoutGuide,它只是iOS11,我需要支持回iOS9 –

+0

這個和iPhone X沒有任何問題嗎?當顯示在iPhone X上時,鍵盤的Y位置與實際屏幕相比似乎有點過高。 – Jonny

3

如果您需要幫助回到前IOS11的版本,你可以 從法比奧使用的功能,並添加:

if #available(iOS 11.0, *) { } 

最終的解決方案:

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) 
     } 
    } 
}