简体   繁体   English

如何同时获取所选UITextField的键盘大小和CGFrame的大小?

[英]How do I get the size of the keyboard and the CGFrame of the selected UITextField at the same time?

I'm populating a vertical UIScrollView with many UITextField fields dynamically on runtime. 我在运行时动态地用许多UITextField字段填充垂直UIScrollView The problem I have is that the keyboard will hide the fields that are in the area where it will appear, this may include the field I'm editing. 我的问题是键盘将隐藏将出现的区域中的字段,其中可能包括我正在编辑的字段。

I tried KeyboardManagement solution from the apple documentation and also tried with notifications on the textFieldDidBeginEditing and textFieldDidEndEditing but the problem in both cases is that the keyboardWillShow notification comes first sometimes, and in that case it doesn't let me know which field is the one being edited. 我尝试了苹果文档中的KeyboardManagement解决方案,还尝试了关于textFieldDidBeginEditingtextFieldDidEndEditing通知,但是两种情况下的问题是,有时有时首先出现keyboardWillShow通知,在这种情况下,它不让我知道哪个字段是编辑。

I have this code in a class that implements the UITextFieldDelegate protocol, each object of this class holds a reference to one of those fields and works as it's delegate 我在实现UITextFieldDelegate协议的类中有这段代码,此类的每个对象都持有对其中一个字段的引用,并作为其委托工作

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

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

The activeTextfield variable is a weak reference to the variable in the UIViewController where all of this happens. activeTextfield变量是对所有发生这种情况的UIViewController中的变量的弱引用。 In that view controller I have the following code 在该视图控制器中,我有以下代码

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

Is there any way I can force the UITextField delegate methods to be called before the keyboard notification? 有什么方法可以强制在键盘通知之前调用UITextField委托方法?

Is this the correct way to handle this kind of situation? 这是处理这种情况的正确方法吗?

If not, how should I handle it? 如果没有,我应该如何处理?

Thanks 谢谢

As stated in your question: 如您的问题所述:

the problem in both cases is that the keyboardWillShow notification comes first sometimes, and in that case it doesn't let me know which field is the one being edited 两种情况下的问题都是有时会首先出现keyboardWillShow通知,在这种情况下,它不会让我知道哪个字段正在被编辑

As per the sequence of events described in apple's documentation, textFieldShouldBeginEditing is the first delegate method called. 根据苹果文档中描述的事件顺序textFieldShouldBeginEditing是第一个调用的委托方法。

So, you can 所以你可以

  1. implement textFieldShouldBeginEditing in the delegate to set your active text field, instead of textFieldDidBeginEditing (make sure you return true from textFieldShouldBeginEditing to allow editing) 在委托中实现textFieldShouldBeginEditing来设置活动文本字段,而不是textFieldDidBeginEditing (确保从textFieldShouldBeginEditing return true以允许编辑)
  2. use keyboardDidShowNotification instead of keyboardWillShowNotification . 使用keyboardDidShowNotification代替keyboardWillShowNotification

This will ensure you have your UITextField marked before getting the keyboard frame / details. 这将确保在获取键盘框架/细节之前,已对UITextField标记。

You can do so fairly simply by doing the following. 您可以通过以下操作相当简单地完成此操作。 First add notification observers in your view will appear. 首先将出现您视图中的添加通知观察者。

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

Then in your selector function you can have something like this 然后在选择器函数中可以有类似这样的内容

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

And your getSelectedTextField() extension looks like this 您的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
    }
}

您需要为textFields定义标签属性,并检查textFieldDidBeginEditingtextFieldDidEndEditing调用了什么UITextField

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM