Pobieranie rozmiaru klawiatury z userInfo w Swift

82

Próbowałem dodać kod, aby przesunąć widok w górę, gdy pojawia się klawiatura, jednak mam problemy z próbą przetłumaczenia przykładów Objective-C na Swift. Zrobiłem pewne postępy, ale utknąłem na jednej konkretnej linii.

Oto dwa samouczki / pytania, które obserwowałem:

Jak przenieść zawartość UIViewController w górę, gdy klawiatura pojawia się za pomocą Swift http://www.ioscreator.com/tutorials/move-view-when-keyboard-appears

Oto kod, który obecnie mam:

override func viewWillAppear(animated: Bool) {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}

override func viewWillDisappear(animated: Bool) {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillShow(notification: NSNotification) {
    var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
    UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
    let frame = self.budgetEntryView.frame
    frame.origin.y = frame.origin.y - keyboardSize
    self.budgetEntryView.frame = frame
}

func keyboardWillHide(notification: NSNotification) {
    //
}

W tej chwili otrzymuję błąd w tej linii:

var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))

Gdyby ktoś mógł mi powiedzieć, jaki powinien być ten wiersz kodu, resztę powinienem sam wymyślić.

user3746428
źródło

Odpowiedzi:

186

W Twojej linii jest kilka problemów:

var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
  • notification.userInfozwraca opcjonalny słownik [NSObject : AnyObject]?, więc przed uzyskaniem dostępu do jego wartości należy go rozpakować.
  • Objective-C NSDictionaryjest odwzorowany na Swift native Dictionary, więc dict[key]aby uzyskać dostęp do wartości , należy użyć składni indeksu dolnego słownika ( ).
  • Wartość musi być rzutowana, aby NSValuemożna było ją wywołać CGRectValue.

Wszystko to można osiągnąć dzięki kombinacji opcjonalnego przypisania, opcjonalnego łączenia w łańcuch i opcjonalnych rzutów:

if let userInfo = notification.userInfo {
   if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
    let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
       // ...
   } else {
       // no UIKeyboardFrameBeginUserInfoKey entry in userInfo
   }
} else {
   // no userInfo dictionary in notification
}

Lub w jednym kroku:

if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
    let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
    // ...
}

Aktualizacja dla Swift 3.0.1 (Xcode 8.1):

if let userInfo = notification.userInfo {
    if let keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
        let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
        // ...
    } else {
        // no UIKeyboardFrameBeginUserInfoKey entry in userInfo
    }
} else {
    // no userInfo dictionary in notification
}

Lub w jednym kroku:

if let keyboardSize = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
    let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
    // ...
}

Aktualizacja dla Swift 5 (Xcode 11.6):

 guard let userInfo = notification.userInfo,
              let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }

Zalecam używanie keyboardFrameEndUserInfoKeyzamiast, keyboardFrameBeginUserInfoKeyponieważ klawiatura zmienia początkową wysokość renderowania po pierwszym wyświetleniu na starszych urządzeniach z iOS.

Martin R.
źródło
@MartinR Przepraszam, że skomentowałem zły post :) przepraszam
Lamour
Cześć, próbuję uzyskać rozmiar klawiatury z powiadomieniami, ale nie mogę go uruchomić. Dodaję obserwatora w viewDidload (również próbowałem viewWillAppear) NSNotificationCenter.defaultCenter (). AddObserver (self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) Ale metoda nie jest wywoływana. Wypróbowałem to na prawdziwym urządzeniu i symulatorze. Jakakolwiek rada? Dziękuję Ci bardzo.
theMouse
W odpowiedzi „one step” znajduje się wartość cgRectValue, ale powinna to być wartość CGRectValue
Vladimirs Matusevics
@krotov Pierwsza część odpowiedzi dotyczy Swift 2, druga część Swift 3. Ta właściwość została zmieniona między tymi wydaniami.
Martin R,
1
Myślę, że lepiej jest użyć UIKeyboardFrameEndUserInfoKey zamiast UIKeyboardFrameBeginUserInfoKey w przypadku, gdy zmienia się ramka klawiatury (włączona funkcja przewidywania lub przełącz się na klawiaturę emoji dla iOS 9 lub nowszego)
crcalin
18

Aby uzyskać jeszcze mniej kodu, rozważ spojrzenie na TO

To było dla mnie bardzo pomocne. Musisz po prostu uwzględnić ograniczenie widoku w kontrolerze widoku i użyć dwóch dodanych obserwatorów. Następnie użyj następujących metod (przypuszcza się, że tutaj przenosisz tableView)

func keyboardWillShow(sender: NSNotification) {
        if let userInfo = sender.userInfo {
            if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height {
                tableViewBottomConstraint.constant = keyboardHeight
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    self.view.layoutIfNeeded()
                })
            }
        }
    }

i

func keyboardWillHide(sender: NSNotification) {
if let userInfo = sender.userInfo {
  if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height {
    tableViewBottomConstraint.constant = 0.0
    UIView.animateWithDuration(0.25, animations: { () -> Void in self.view.layoutIfNeeded() })
  }
} }
Mikołaja
źródło
1
Nie otrzymałem tej odpowiedzi, dopóki nie zobaczyłem jej gdzie indziej, gdzie stało się dla mnie jasne, że tableViewBottomConstraint jest gniazdem dla Xib. Wtedy stało się jasne, że to doskonała odpowiedź! (Jeśli korzystasz z automatycznego układu)
Joris van Liempd iDeveloper
@JorisvanLiempd Tak, używam układu automatycznego. Dobrze, że ci pomogło.
Nicholas
Wygląda na to, że animacja jest bezpłatna bez bloku animacji. który w tej odpowiedzi i tak nie jest zgodny z krzywą klawiatury i czasem trwania.
AmitP
11

Jeśli używasz scenorysu, zamiast manipulować samym widokiem, możesz skorzystać z automatycznego układu.

(To jest oczyszczona wersja odpowiedzi Nicholasa)

Skonfiguruj centrum powiadomień, aby powiadamiało Cię o pojawieniu się i zniknięciu klawiatury:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)

}

I upewnij się, że usuwasz obserwatory, gdy ich już nie potrzebujesz:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
}

W serii ujęć ustaw dolne ograniczenie. Utwórz ujście tego ograniczenia:

wprowadź opis obrazu tutaj

i ustaw stałą właściwość ograniczenia, gdy klawiatura jest pokazywana lub ukrywana:

func keyboardWillShow(notification: NSNotification) {
    guard let keyboardHeight = (notification.userInfo! as NSDictionary).objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue.size.height else {
        return
    }
    nameOfOutlet.constant = keyboardHeight
    view.layoutIfNeeded()
}

func keyboardWillHide(notification: NSNotification) {
    nameOfOutlet.constant = 0.0
    view.layoutIfNeeded()
}

Teraz, gdy klawiatura pojawi się lub zniknie, autoukład zajmie się wszystkim.

DudeOnRock
źródło
4

Szybki 2

func keyboardWasShown(notification:NSNotification) {
        guard let info:[NSObject:AnyObject] = notification.userInfo,
            let keyboardSize:CGSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size else { return }

        let insets:UIEdgeInsets = UIEdgeInsetsMake(self.scrollView.contentInset.top, 0.0, keyboardSize.height, 0.0)

        self.scrollView.contentInset = insets
        self.scrollView.scrollIndicatorInsets = insets
    }

Szybki 3

func keyboardWasShown(notification:NSNotification) {
    guard let info:[AnyHashable:Any] = notification.userInfo,
        let keyboardSize:CGSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size else { return }

    let insets:UIEdgeInsets = UIEdgeInsets(top: self.scrollView.contentInset.top, left: 0.0, bottom: keyboardSize.height, right: 0.0)

    self.scrollView.contentInset = insets
    self.scrollView.scrollIndicatorInsets = insets
}
Barlow Tucker
źródło
Dzięki, bardzo mi to pomogło!
javimuu
3

Pomogło mi to: https://developer.apple.com/library/ios/samplecode/UICatalog/Listings/Swift_UICatalog_TextViewController_swift.html

let userInfo = notification.userInfo!

let animationDuration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as NSNumber).doubleValue
let keyboardScreenBeginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as NSValue).CGRectValue()
let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as NSValue).CGRectValue()
Paweł
źródło
1

Możesz użyć tej jednej linii dla swojej linii

var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
Avinash
źródło
3
Wymuszanie rozpakowywania ramki klawiatury z tego słownika nie jest bezpieczne. Nie mogło tam być.
Adama
1

Swift 3: AKTUALIZACJA

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}
Kevin Sabbe
źródło
1

Swift - wysokość klawiatury z keyboardWillShowNotification

Możesz zwiększyć lub zmniejszyć ograniczenie lub dowolną inną wartość do rozmiaru klawiatury, używając danych z klawiatury. Pokaż / ukryje powiadomienia.

Z ograniczeniem układu

Ten minimalny kod rejestruje się w celu powiadomienia, że ​​klawiatura pokaże i zaktualizuje ograniczenie w oparciu o jego rozmiar.

@IBOutlet weak var keyboardConstraint: NSLayoutConstraint!
let keyboardConstraintMargin:CGFloat = 20

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { (notification) in
        if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect {
            self.keyboardConstraint.constant = keyboardSize.height + self.keyboardConstraintMargin
        }
    }
    NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, object: nil, queue: nil) { (notification) in
        self.keyboardConstraint.constant = self.keyboardConstraintMargin
    }
}

Z ScrollView

W ten sam sposób aktualizuje wstawkę zawartości widoku przewijania na podstawie rozmiaru klawiatury.

@IBOutlet weak var scrollView: UIScrollView!

override func viewDidLoad() {
  super.viewDidLoad()
  NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { (notification) in
    if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect {
      let insets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
      self.scrollView.contentInset = insets
      self.scrollView.scrollIndicatorInsets = insets
    }
  }
  NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, object: nil, queue: nil) { (notification) in
    let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    self.scrollView.contentInset = insets
    self.scrollView.scrollIndicatorInsets = insets
  }
}
timeSmith
źródło
1

Detale

  • Xcode w wersji 11.1 (11A1027), iOS 13, Swift 5

Rozwiązanie

import UIKit

protocol KeyboardNotificationsDelegate: class {
    func keyboardWillShow(notification: NSNotification)
    func keyboardWillHide(notification: NSNotification)
    func keyboardDidShow(notification: NSNotification)
    func keyboardDidHide(notification: NSNotification)
}

extension KeyboardNotificationsDelegate {
    func keyboardWillShow(notification: NSNotification) {}
    func keyboardWillHide(notification: NSNotification) {}
    func keyboardDidShow(notification: NSNotification) {}
    func keyboardDidHide(notification: NSNotification) {}
}

class KeyboardNotifications {

    fileprivate var _isEnabled: Bool
    fileprivate var notifications: [KeyboardNotificationsType]
    fileprivate weak var delegate: KeyboardNotificationsDelegate?

    init(notifications: [KeyboardNotificationsType], delegate: KeyboardNotificationsDelegate) {
        _isEnabled = false
        self.notifications = notifications
        self.delegate = delegate
    }

    deinit { if isEnabled { isEnabled = false } }
}

// MARK: - enums

extension KeyboardNotifications {

    enum KeyboardNotificationsType {
        case willShow, willHide, didShow, didHide

        var selector: Selector {
            switch self {
                case .willShow: return #selector(keyboardWillShow(notification:))
                case .willHide: return #selector(keyboardWillHide(notification:))
                case .didShow: return #selector(keyboardDidShow(notification:))
                case .didHide: return #selector(keyboardDidHide(notification:))
            }
        }

        var notificationName: NSNotification.Name {
            switch self {
                case .willShow: return UIResponder.keyboardWillShowNotification
                case .willHide: return UIResponder.keyboardWillHideNotification
                case .didShow: return UIResponder.keyboardDidShowNotification
                case .didHide: return UIResponder.keyboardDidHideNotification
            }
        }
    }
}

// MARK: - isEnabled

extension KeyboardNotifications {

    private func addObserver(type: KeyboardNotificationsType) {
        NotificationCenter.default.addObserver(self, selector: type.selector, name: type.notificationName, object: nil)
    }

    var isEnabled: Bool {
        set {
            if newValue {
                for notificaton in notifications { addObserver(type: notificaton) }
            } else {
                NotificationCenter.default.removeObserver(self)
            }
            _isEnabled = newValue
        }

        get { return _isEnabled }
    }

}

// MARK: - Notification functions

extension KeyboardNotifications {

    @objc func keyboardWillShow(notification: NSNotification) {
        delegate?.keyboardWillShow(notification: notification)
    }

    @objc func keyboardWillHide(notification: NSNotification) {
        delegate?.keyboardWillHide(notification: notification)
    }

    @objc func keyboardDidShow(notification: NSNotification) {
        delegate?.keyboardDidShow(notification: notification)
    }

    @objc func keyboardDidHide(notification: NSNotification) {
        delegate?.keyboardDidHide(notification: notification)
    }
}

Stosowanie

class ViewController: UIViewController {

    private lazy var keyboardNotifications: KeyboardNotifications! = {
        return KeyboardNotifications(notifications: [.willShow, .willHide, .didShow, .didHide], delegate: self)
    }()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        keyboardNotifications.isEnabled = true
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        keyboardNotifications.isEnabled = false
    }
}

extension ViewController: KeyboardNotificationsDelegate {

    // If you don't need this func you can remove it
    func keyboardWillShow(notification: NSNotification) {
        print("keyboardWillShow")
        guard   let userInfo = notification.userInfo as? [String: NSObject],
                let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
        print("keyboardFrame: \(keyboardFrame)")
    }

    // If you don't need this func you can remove it
    func keyboardWillHide(notification: NSNotification) { print("keyboardWillHide") }

    // If you don't need this func you can remove it
    func keyboardDidShow(notification: NSNotification) { print("keyboardDidShow") }

    // If you don't need this func you can remove it
    func keyboardDidHide(notification: NSNotification) { print("keyboardDidHide") }
}

Pełna próbka

import UIKit

class ViewController: UIViewController {

    private lazy var keyboardNotifications: KeyboardNotifications! = {
        return KeyboardNotifications(notifications: [.willShow, .willHide, .didShow, .didHide], delegate: self)
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        let textField = UITextField(frame: CGRect(x: 40, y: 40, width: 200, height: 30))
        textField.borderStyle = .roundedRect
        view.addSubview(textField)

        let gesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
        view.addGestureRecognizer(gesture)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        keyboardNotifications.isEnabled = true
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        keyboardNotifications.isEnabled = false
    }
}

 extension ViewController: KeyboardNotificationsDelegate {

    // If you don't need this func you can remove it
    func keyboardWillShow(notification: NSNotification) {
        print("keyboardWillShow")
        guard   let userInfo = notification.userInfo as? [String: NSObject],
                let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
        print("keyboardFrame: \(keyboardFrame)")
    }

    // If you don't need this func you can remove it
    func keyboardWillHide(notification: NSNotification) { print("keyboardWillHide") }

    // If you don't need this func you can remove it
    func keyboardDidShow(notification: NSNotification) { print("keyboardDidShow") }

    // If you don't need this func you can remove it
    func keyboardDidHide(notification: NSNotification) { print("keyboardDidHide") }
}

Wynik

wprowadź opis obrazu tutaj

Log

wprowadź opis obrazu tutaj

Wasilij Bodnarczuk
źródło
0

Swift 3.0

Oto przykład pobierania rozmiaru klawiatury i używania go do animowania widoku w górę. W moim przypadku przesuwam UIView zawierający moje UITextFields w górę, gdy użytkownik zaczyna pisać, aby mógł wypełnić formularz i nadal widzieć przycisk przesyłania u dołu.

Dodałem wylot do ograniczenia dolnej przestrzeni widoku, który chciałem animować i nazwałam go myViewsBottomSpaceConstraint:

@IBOutlet weak var myViewsBottomSpaceConstraint: NSLayoutConstraint!

Następnie dodałem następujący kod do mojej klasy Swift:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}

func keyboardWillShow(notification: NSNotification) {

    let userInfo = notification.userInfo as! [String: NSObject] as NSDictionary
    let keyboardFrame = userInfo.value(forKey: UIKeyboardFrameEndUserInfoKey) as! CGRect
    let keyboardHeight = keyboardFrame.height
    myViewsBottomSpaceConstraint.constant = keyboardHeight
    view.layoutIfNeeded()
}

func keyboardWillHide(notification: NSNotification) {
    myViewsBottomSpaceConstraint.constant = 0.0
    view.layoutIfNeeded()
}
mobilecat
źródło
0

W przypadku platformy Xamarin można użyć języka C # 6

private void KeyboardWillChangeFrame(NSNotification notification)
{
        var keyboardSize = notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey) as NSValue;
        if (keyboardSize != null)
        {
            var rect= keyboardSize.CGRectValue;
            //do your stuff here
        }
}

c # 7

  private void KeyboardWillChangeFrame(NSNotification notification)
   {
       if (!(notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey) is NSValue keyboardSize)) return;
       var rect= keyboardSize.CGRectValue;
   }
marcel
źródło
0

w Swift 4.2 możesz użyć UIResponder.keyboardFrameEndUserInfoKey

guard let userInfo = notification.userInfo , let keyboardFrame:CGRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect  else { return  }```
Aybek Can Kaya
źródło