iosswiftuiviewuitextfielduiresponder

How do I display the keyboard and move up the UIView at the same time when the view appears?


I've created a view controller with a UIView containing a text field (Note: the view controller appears as a modal). When you tap into the text field, a keyboard appears and the UIView moves up the screen so that the text field is not obscured. However, my goal is to display the keyboard and the (unobscured) UIView from the very beginning when the modal initially appears, which I'm struggling to achieve.

I've tried inserting textField.becomeFirstResponder() into viewDidLoad, but this displays the keyboard without moving the UIView to its desired (i.e. visible) position. I've also tried inserting this into viewDidAppear, but this displays the UIView first, then stalls for a second, before displaying the keyboard and moving up the UIView in a very awkward transition.

It would be great if anyone could provide some suggestions to solve this issue. My code is below.

@IBOutlet var textField: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .darkGray
    
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    //The line below will display the keyboard over the UIView, thus obscuring it.
    textField.becomeFirstResponder()
}

@objc func keyboardWillShow(_ notification: Notification) {
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        let keyboardRectangle = keyboardFrame.cgRectValue
        let keyboardHeight = keyboardRectangle.height
    }
    let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
    self.view.frame.origin.y = 0 - keyboardSize!.height
}

Below is a visual reference.

enter image description here


Solution

  • Make a reference (IBOutlet) of your view's bottom constraint, name it as bottomConstraint or whatever you prefer.

    Then as what you're doing in your keyboardWillShow selector, extract the keyboard height, and assign that height to the constant property of your bottomConstraint. Add animations if you want.

    import UIKit
    
    class SecondViewController: UIViewController {
    
        @IBOutlet var textField: UITextField!
        @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .darkGray
            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
            
            //The line below will display the keyboard over the UIView, thus obscuring it.
            textField.becomeFirstResponder()
        }
    
        @objc func keyboardWillShow(_ notification: Notification) {
            let info = notification.userInfo!
            let kbHeight = (info[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
            
            bottomConstraint.constant = -kbHeight
            
            let duration: TimeInterval = (info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
            
            UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() }
        }
    }
    

    enter image description here