1
votes

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

2

2 Answers

0
votes

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

0
votes

You can add animation block for your view to look better, also you will need to match the speed for the keyboard animation like this:

        if let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double {
            UIView.animate(withDuration: duration, animations: { [weak self] in
                self?.view.layoutIfNeeded()
            })
        }

This should be added at the end of your keyboardWillShow func.

Also may be it would be better if you move the textField.becomeFirstResponder() to the viewWillAppear.