2017-07-30 39 views
5

Nowy przewodnik po układzie Bezpiecznego obszaru wprowadzony w iOS 11 działa świetnie, aby zapobiec wyświetlaniu zawartości pod pasami, ale nie obejmuje klawiatury. Oznacza to, że gdy wyświetlana jest klawiatura, zawartość jest nadal ukryta za nią i jest to problem, który próbuję rozwiązać.Poszerzenie obszaru bezpiecznego iOS 11 o klawiaturę

Moje podejście polega na słuchaniu powiadomień na klawiaturze, a następnie dostosowywaniu bezpiecznego obszaru poprzez additionalSafeAreaInsets.

Oto mój kod:

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

Działa to także MyController jest UIViewController z UITableView który rozciąga się przez całą bezpiecznej okolicy. Teraz, gdy pojawia się klawiatura, spód jest wypychany w górę, tak aby żadne komórki nie znajdowały się za klawiaturą.

Problem dotyczy pasków dolnych. Mam również pasek narzędzi na dole, który jest już zawarty w bezpiecznym obszarze. Dlatego ustawienie pełnej wysokości klawiatury jako dodatkowej wstawki w bezpiecznym obszarze powoduje przesunięcie dolnej części widoku tabeli zbyt dokładnie o wysokość dolnego paska. Aby ta metoda działała dobrze, muszę ustawić wartość additionalSafeAreaInsets.bottom na wysokość klawiatury minus wysokość dolnego paska.

Pytanie 1: Jaki jest najlepszy sposób na uzyskanie obecnej luki w obszarze bezpieczeństwa na dole? Ręcznie pobrać ramkę paska narzędzi i użyć jej wysokości? Czy możliwe jest uzyskanie luki bezpośrednio z przewodnika układu bezpiecznego obszaru?

Pytanie 2: Możliwe, że dolny pasek będzie mógł zmienić rozmiar bez zmiany rozmiaru klawiatury, więc powinienem również wdrożyć metodę słuchania zmian w ramce paska. Czy najlepiej to zrobić w viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)? Lub gdzie indziej?

Dziękuję

Odpowiedz

6

Co wydaje się działać dla mnie jest obliczenie przecięcia między view.safeAreaLayoutGuide.layoutFrame i ramkę klawiatury, a następnie ustawiając wysokość, że jako additionalSafeAreaInsets.bottom, zamiast całej wysokości ramy klawiatury. Nie mam paska narzędzi w moim kontrolerem widoku, ale mam pasek kart i jest on księgowany poprawnie.

Kompletny kod:

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

To jest lepsze rozwiązanie, aby poradzić sobie z iPhoneX, ponieważ jego wysokość obejmuje poza bezpiecznym obszarem –

+0

... ale niestety używa safeAreaLayoutGuide, który jest tylko iOS11 i muszę wesprzeć z powrotem do iOS9 –

+0

Nie masz żadnych problemów z tym i iPhone X? Pozycja Y klawiatury, gdy jest wyświetlana na telefonie iPhone X, wydaje się nieco zbyt duża w porównaniu do rzeczywistego ekranu. – Jonny

3

Jeśli potrzebują wsparcia z powrotem do pre IOS11 wersje można użyć funkcji od Fabio i dodać:

if #available(iOS 11.0, *) { } 

ostateczne rozwiązanie:

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