2017-07-30 17 views
5

iOS 11で導入された新しいセーフエリアレイアウトガイドは、コンテンツがバーの下に表示されないように機能しますが、キーボードは除きます。つまり、キーボードが表示されても、コンテンツはそれの後ろに隠れており、これが私が解決しようとしている問題です。キーボードを含めるためにiOS 11セーフエリアを拡張する

私のアプローチは、キーボード通知を聞いてから、安全領域をadditionalSafeAreaInsetsに調整することに基づいています。ここで

は私のコードです:MyControllerは全体の安全なエリアを通って延びているUITableViewUIViewControllerあるよう

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

これはうまく動作します。キーボードが現れたら、底部が押し上げられて、キーボードの後ろにセルがないようにします。

問題は下部バーにあります。私はまた、セーフエリアに既に含まれている下部にツールバーがあります。したがって、キーボードの高さを完全に安全な領域に設定すると、テーブルビューの下端がボトムバーの高さだけ押し上げられます。この方法をうまく機能させるには、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

...残念ながら、それはiOS11専用のsafeAreaLayoutGuideを使用しており、iOS9に戻ってサポートする必要があります –

+0

これとiPhone Xに問題はありませんか?キーボードのY位置がiPhone Xに表示されていると、実際の画面に比べて少し高いようです。 – 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) 
     } 
    } 
} 
関連する問題