How to Move a View Up and Down with the Keyboard in Swift

When you have multiple UITextFields on a screen, have you ever had trouble typing in a text field at the bottom because the text field is hidden by the keyboard?

In this tutorial, we'll go over how to move the entire view up and down along with the keyboard in Swift, so that you can still type into UITextFields at the bottom of the screen.

Create a Test App

First, let's create a simple test app.

In Xcode, create a new project using [iOS][App].

On the storyboard, place three Text Fields (top, middle, bottom) and a Button underneath them, as shown below.

Move View with Keyboard 1


Create an outlet for the bottom Text Field that you want to move the view for when editing begins.

Here, we'll name the outlet textField3.

Move View with Keyboard 2


Dismiss the Keyboard When Tapping Outside

We also want the keyboard to close when tapping outside the Text Field. Add the following code to ViewController.swift:

For a detailed explanation of this code, see How to Dismiss the Keyboard When Tapping Outside.

class ViewController: UIViewController {
    
    @IBOutlet weak var textField3: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tapGR: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        tapGR.cancelsTouchesInView = false
        self.view.addGestureRecognizer(tapGR)
    }
    
    @objc func dismissKeyboard() {
        self.view.endEditing(true)
    }
}

Move the View Up and Down with the Keyboard

Now let's implement the code to move the entire view up and down with the keyboard when the bottom Text Field is edited.

Add the following code to ViewController.swift:

class ViewController: UIViewController {
    
    @IBOutlet weak var textField3: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tapGR: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        tapGR.cancelsTouchesInView = false
        self.view.addGestureRecognizer(tapGR)
        
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    @objc func dismissKeyboard() {
        self.view.endEditing(true)
    }
    
    @objc func keyboardWillShow(notification: NSNotification) {
        if !textField3.isFirstResponder {
            return
        }
    
        if self.view.frame.origin.y == 0 {
            if let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
                self.view.frame.origin.y -= keyboardRect.height
            }
        }
    }
    
    @objc func keyboardWillHide(notification: NSNotification) {
        if self.view.frame.origin.y != 0 {
            self.view.frame.origin.y = 0
        }
    }
}

At line 12, we use NotificationCenter.default.addObserver() to register an observer that listens for keyboardWillShowNotification when the keyboard opens, and then calls the keyboardWillShow function.

At line 13, we similarly use NotificationCenter.default.addObserver() to register an observer that listens for keyboardWillHideNotification when the keyboard closes, and then calls the keyboardWillHide function.


Lines 20–30 define the keyboardWillShow function, which is executed when the keyboard opens.

First, if the keyboard was opened for a UITextField other than textField3, we don't want the view to move. So we check with textField3.isFirstResponder to confirm that the keyboard opened for textField3. If not, we simply return.

Then, if the y-origin of the ViewController's view is 0 (meaning the view hasn't been moved up yet), line 26 retrieves the keyboard size, and line 27 subtracts the keyboard height from the view's y-origin, effectively moving the entire view upward by the keyboard height.


Lines 32–36 define the keyboardWillHide function, which is executed when the keyboard closes.

Here, if the y-origin of the ViewController's view is not 0 (meaning the view has been moved upward), we reset the y-origin back to 0, returning the view to its original position.

If you're using a Navigation Controller, depending on the navigation bar's configuration, the y-origin of the view may not start at 0. In that case, you should use the position that accounts for the navigation bar, such as the value from self.navigationController!.navigationBar.frame.maxY, instead of 0.

@objc func keyboardWillShow(notification: NSNotification) {
    if !textField3.isFirstResponder {
        return
    }

    let originY = self.navigationController!.navigationBar.frame.maxY
    if self.view.frame.origin.y == originY {
        if let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            self.view.frame.origin.y -= keyboardRect.height
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    let originY = self.navigationController!.navigationBar.frame.maxY
    if self.view.frame.origin.y != originY {
        self.view.frame.origin.y = originY
    }
}

With this, as shown in the video earlier, when you try to type into a UITextField located at the bottom of the screen, the entire view will move up and down along with the keyboard.

According to Apple's official documentation, if your target is iOS 9.0 or later, when registering observers using addObserver(_:selector:name:object:) as shown here, you do not need to manually remove them with removeObserver().

Move View with Keyboard 5


Handling Button Events When the View Moves

One thing you need to be careful about is how button events behave when the button is tapped, the keyboard closes, and the view moves back down.

When adding button actions, most developers may use the default TouchUpInside.

In this case, however, TouchUpInside does not occur because the button itself moves at the moment it is tapped.


Let's add three actions—TouchUpInside, TouchUpOutside, and TouchDown—to the OK button and test what happens.

Move View with Keyboard 3


When the keyboard is closed and you tap the OK button, the console prints:

TouchDown
TouchUpInside

When you tap textField3 to open the keyboard and then click the OK button, the console prints:

TouchDown
TouchUpOutside

This happens because the OK button moves along with the view, so when you lift your finger, the release position is outside the button. As a result, TouchUpOutside is triggered instead of TouchUpInside.

In cases where the button moves along with the keyboard, if it's acceptable for the action to trigger immediately upon tapping, it's better to use the TouchDown event.


That concludes the explanation of how to move the entire view up and down along with the keyboard in Swift.