Как получить размер клавиатуры и CGFrame выбранного UITextField одновременно? - PullRequest
0 голосов
/ 26 апреля 2019

Я динамически заполняю UIScrollView многими полями UITextField во время выполнения.У меня проблема в том, что клавиатура будет скрывать поля, которые находятся в той области, где она появится, это может включать поле, которое я редактирую.

Я попытался KeyboardManagement решение отДокументация Apple, а также пробовал с уведомлениями на textFieldDidBeginEditing и textFieldDidEndEditing, но проблема в обоих случаях заключается в том, что уведомление keyboardWillShow иногда появляется первым, и в этом случае оно не дает мне знать, какое поле являетсяОтредактировано.

У меня есть этот код в классе, который реализует протокол UITextFieldDelegate, каждый объект этого класса содержит ссылку на одно из этих полей и работает как его делегат

func textFieldDidBeginEditing(_ textField: UITextField) {
    self.activeTextfield = self.valueTextField
}

func textFieldDidEndEditing(_ textField: UITextField) {
    self.activeTextfield = nil
}

Переменная activeTextfield является слабой ссылкой на переменную в UIViewController, где все это происходит.В этом контроллере представления у меня есть следующий код

class MyClass: UIViewController {
    var activeTextfield: CustomTextField! // This is the variable I was talking about on the previous paragraph

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        NotificationCenter.default.removeObserver(self)
    }

    @objc func keyboardWillShow(_ notification: Notification) {
        if self.view.frame.origin.y == 0 {
            guard let userInfo = notification.userInfo else { return }
            guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

            let keyboardFrame = keyboardSize.cgRectValue
            let textFieldFrame = activeTextfield!.frame // activeTextfield sometimes is nil because this notification happens before the previous code block

            if textFieldFrame.origin.y + textFieldFrame.size.height > keyboardFrame.origin.y {
                self.view.frame.origin.y -= keyboardFrame.height
            }
        }
    }

    @objc func keyboardWillHide(_ notification: Notification) {
        if self.view.frame.origin.y != 0 {
            guard let userInfo = notification.userInfo else { return }
            guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

            let keyboardFrame = keyboardSize.cgRectValue

            self.view.frame.origin.y += keyboardFrame.height
        }
    }
}

Есть ли способ заставить методы делегирования UITextField вызываться до уведомления клавиатуры?

Это правильный путь?справиться с такой ситуацией?

Если нет, как мне справиться с этим?

Спасибо

Ответы [ 3 ]

2 голосов
/ 26 апреля 2019

Как указано в вашем вопросе:

проблема в обоих случаях в том, что клавиатура уведомит WillShow иногда приходит первым, и в этом случае это не дает мне знать, какие поле редактируется

В соответствии с последовательностью событий , описанной в документации Apple, textFieldShouldBeginEditing - это первый вызванный метод делегата.

Итак, вы можете

  1. внедрите textFieldShouldBeginEditing в делегате для установки активного текстового поля вместо textFieldDidBeginEditing (убедитесь, что вы return true из textFieldShouldBeginEditing разрешаете редактирование)
  2. используйте keyboardDidShowNotification вместо keyboardWillShowNotification.

Это обеспечит пометку UITextField перед тем, как получить кадр / детали клавиатуры.

1 голос
/ 27 апреля 2019

Вы можете сделать это довольно просто, выполнив следующее. Сначала добавьте уведомления наблюдателей в вашем представлении.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)        
    // Keyboard notification
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

Тогда в вашей функции выбора вы можете иметь что-то вроде этого

@objc func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
            let currentTextField = view.getSelectedTextField() {

        let keyboardHeight = keyboardSize.height
        let textFieldFrame = currentTextField.superview?.convert(currentTextField.frame, to: nil)            
        }
    }
}

А ваше getSelectedTextField() расширение выглядит так

// Inside UIView Extension 
// Get currently active textfield
func getSelectedTextField() -> UITextField? {

    let totalTextFields = getTextFieldsInView(view: self)

    for textField in totalTextFields{
        if textField.isFirstResponder{
            return textField
        }
    }
    return nil
}

    func getTextFieldsInView(view: UIView) -> [UITextField] {

        var totalTextFields = [UITextField]()

        for subview in view.subviews as [UIView] {
            if let textField = subview as? UITextField {
                totalTextFields += [textField]
            } else {
                totalTextFields += getTextFieldsInView(view: subview)
            }
        }
        return totalTextFields
    }
}
0 голосов
/ 26 апреля 2019

Вам необходимо определить свойство тега для ваших textFields и отметить textFieldDidBeginEditing и textFieldDidEndEditing как назывался UITextField.

...